001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.oauth2.sdk.device; 019 020 021import java.net.URI; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.Set; 025 026import net.jcip.annotations.Immutable; 027import net.minidev.json.JSONObject; 028 029import com.nimbusds.common.contenttype.ContentType; 030import com.nimbusds.oauth2.sdk.ErrorObject; 031import com.nimbusds.oauth2.sdk.ErrorResponse; 032import com.nimbusds.oauth2.sdk.OAuth2Error; 033import com.nimbusds.oauth2.sdk.ParseException; 034import com.nimbusds.oauth2.sdk.http.HTTPResponse; 035import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 036 037 038/** 039 * OAuth 2.0 device authorization error response. 040 * 041 * <p>Standard authorization errors: 042 * 043 * <ul> 044 * <li>{@link OAuth2Error#INVALID_REQUEST} 045 * <li>{@link OAuth2Error#INVALID_CLIENT} 046 * <li>{@link OAuth2Error#INVALID_SCOPE} 047 * </ul> 048 * 049 * <p>Example HTTP response: 050 * 051 * <pre> 052 * HTTP/1.1 400 Bad Request 053 * Content-Type: application/json 054 * Cache-Control: no-store 055 * Pragma: no-cache 056 * 057 * { 058 * "error" : "invalid_request" 059 * } 060 * </pre> 061 * 062 * <p>Related specifications: 063 * 064 * <ul> 065 * <li>OAuth 2.0 Device Authorization Grant (RFC 8628) 066 * </ul> 067 */ 068@Immutable 069public class DeviceAuthorizationErrorResponse extends DeviceAuthorizationResponse implements ErrorResponse { 070 071 072 /** 073 * The standard OAuth 2.0 errors for a device authorization error response. 074 */ 075 private static final Set<ErrorObject> STANDARD_ERRORS; 076 077 static { 078 Set<ErrorObject> errors = new HashSet<>(); 079 errors.add(OAuth2Error.INVALID_REQUEST); 080 errors.add(OAuth2Error.INVALID_CLIENT); 081 errors.add(OAuth2Error.INVALID_SCOPE); 082 STANDARD_ERRORS = Collections.unmodifiableSet(errors); 083 } 084 085 086 /** 087 * Gets the standard OAuth 2.0 errors for a device authorization error 088 * response. 089 * 090 * @return The standard errors, as a read-only set. 091 */ 092 public static Set<ErrorObject> getStandardErrors() { 093 094 return STANDARD_ERRORS; 095 } 096 097 098 /** 099 * The error. 100 */ 101 private final ErrorObject error; 102 103 104 /** 105 * Creates a new OAuth 2.0 device authorization error response. No 106 * OAuth 2.0 error is specified. 107 */ 108 protected DeviceAuthorizationErrorResponse() { 109 110 error = null; 111 } 112 113 114 /** 115 * Creates a new OAuth 2.0 device authorization error response. 116 * 117 * @param error The error. Should match one of the 118 * {@link #getStandardErrors standard errors} for a token 119 * error response. Must not be {@code null}. 120 */ 121 public DeviceAuthorizationErrorResponse(final ErrorObject error) { 122 123 if (error == null) 124 throw new IllegalArgumentException("The error must not be null"); 125 126 this.error = error; 127 } 128 129 130 @Override 131 public boolean indicatesSuccess() { 132 133 return false; 134 } 135 136 137 @Override 138 public ErrorObject getErrorObject() { 139 140 return error; 141 } 142 143 144 /** 145 * Returns the JSON object for this token error response. 146 * 147 * @return The JSON object for this token error response. 148 */ 149 public JSONObject toJSONObject() { 150 151 JSONObject o = new JSONObject(); 152 153 // No error? 154 if (error == null) 155 return o; 156 157 o.put("error", error.getCode()); 158 159 if (error.getDescription() != null) 160 o.put("error_description", error.getDescription()); 161 162 if (error.getURI() != null) 163 o.put("error_uri", error.getURI().toString()); 164 165 return o; 166 } 167 168 169 @Override 170 public HTTPResponse toHTTPResponse() { 171 172 int statusCode = (error != null && error.getHTTPStatusCode() > 0) ? error.getHTTPStatusCode() 173 : HTTPResponse.SC_BAD_REQUEST; 174 175 HTTPResponse httpResponse = new HTTPResponse(statusCode); 176 177 if (error == null) 178 return httpResponse; 179 180 httpResponse.setEntityContentType(ContentType.APPLICATION_JSON); 181 httpResponse.setCacheControl("no-store"); 182 httpResponse.setPragma("no-cache"); 183 184 httpResponse.setContent(toJSONObject().toString()); 185 186 return httpResponse; 187 } 188 189 190 /** 191 * Parses an OAuth 2.0 device authorization response from the specified 192 * JSON object. 193 * 194 * @param jsonObject The JSON object to parse. Its status code must not 195 * be 200 (OK). Must not be {@code null}. 196 * 197 * @return The token error response. 198 * 199 * @throws ParseException If the JSON object couldn't be parsed to an 200 * OAuth 2.0 device authorization error 201 * response. 202 */ 203 public static DeviceAuthorizationErrorResponse parse(final JSONObject jsonObject) throws ParseException { 204 205 // No error code? 206 if (!jsonObject.containsKey("error")) 207 return new DeviceAuthorizationErrorResponse(); 208 209 ErrorObject error; 210 211 try { 212 // Parse code 213 String code = JSONObjectUtils.getString(jsonObject, "error"); 214 String description = JSONObjectUtils.getString(jsonObject, "error_description", null); 215 URI uri = JSONObjectUtils.getURI(jsonObject, "error_uri", null); 216 217 error = new ErrorObject(code, description, HTTPResponse.SC_BAD_REQUEST, uri); 218 219 } catch (ParseException e) { 220 throw new ParseException("Missing or invalid token error response parameter: " + e.getMessage(), 221 e); 222 } 223 224 return new DeviceAuthorizationErrorResponse(error); 225 } 226 227 228 /** 229 * Parses an OAuth 2.0 ciba authorization error, push error or token response - from the 230 * specified HTTP response. 231 * 232 * @param httpResponse The HTTP response to parse. Its status code must 233 * not be 200 (OK). Must not be {@code null}. 234 * 235 * @return The device authorization error response. 236 * 237 * @throws ParseException If the HTTP response couldn't be parsed to an 238 * OAuth 2.0 device authorization error 239 * response. 240 */ 241 public static DeviceAuthorizationErrorResponse parse(final HTTPResponse httpResponse) throws ParseException { 242 243 httpResponse.ensureStatusCodeNotOK(); 244 return new DeviceAuthorizationErrorResponse(ErrorObject.parse(httpResponse)); 245 } 246}