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; 019 020 021import java.net.URI; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.Set; 025 026import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 027import com.nimbusds.oauth2.sdk.http.HTTPResponse; 028import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 029import net.jcip.annotations.Immutable; 030import net.minidev.json.JSONObject; 031 032 033/** 034 * OAuth 2.0 Token error response. 035 * 036 * <p>Standard token errors: 037 * 038 * <ul> 039 * <li>{@link OAuth2Error#INVALID_REQUEST} 040 * <li>{@link OAuth2Error#INVALID_CLIENT} 041 * <li>{@link OAuth2Error#INVALID_GRANT} 042 * <li>{@link OAuth2Error#UNAUTHORIZED_CLIENT} 043 * <li>{@link OAuth2Error#UNSUPPORTED_GRANT_TYPE} 044 * <li>{@link OAuth2Error#INVALID_SCOPE} 045 * </ul> 046 * 047 * <p>Example HTTP response: 048 * 049 * <pre> 050 * HTTP/1.1 400 Bad Request 051 * Content-Type: application/json 052 * Cache-Control: no-store 053 * Pragma: no-cache 054 * 055 * { 056 * "error": "invalid_request" 057 * } 058 * </pre> 059 * 060 * <p>Related specifications: 061 * 062 * <ul> 063 * <li>OAuth 2.0 (RFC 6749), section 5.2. 064 * </ul> 065 */ 066@Immutable 067public class TokenErrorResponse extends TokenResponse implements ErrorResponse { 068 069 070 /** 071 * The standard OAuth 2.0 errors for an Access Token error response. 072 */ 073 private static final Set<ErrorObject> STANDARD_ERRORS; 074 075 076 static { 077 Set<ErrorObject> errors = new HashSet<>(); 078 errors.add(OAuth2Error.INVALID_REQUEST); 079 errors.add(OAuth2Error.INVALID_CLIENT); 080 errors.add(OAuth2Error.INVALID_GRANT); 081 errors.add(OAuth2Error.UNAUTHORIZED_CLIENT); 082 errors.add(OAuth2Error.UNSUPPORTED_GRANT_TYPE); 083 errors.add(OAuth2Error.INVALID_SCOPE); 084 STANDARD_ERRORS = Collections.unmodifiableSet(errors); 085 } 086 087 088 /** 089 * Gets the standard OAuth 2.0 errors for an Access Token error 090 * response. 091 * 092 * @return The standard errors, as a read-only set. 093 */ 094 public static Set<ErrorObject> getStandardErrors() { 095 096 return STANDARD_ERRORS; 097 } 098 099 100 /** 101 * The error. 102 */ 103 private final ErrorObject error; 104 105 106 /** 107 * Creates a new OAuth 2.0 Access Token error response. No OAuth 2.0 108 * error is specified. 109 */ 110 protected TokenErrorResponse() { 111 112 error = null; 113 } 114 115 116 /** 117 * Creates a new OAuth 2.0 Access Token error response. 118 * 119 * @param error The error. Should match one of the 120 * {@link #getStandardErrors standard errors} for a token 121 * error response. Must not be {@code null}. 122 */ 123 public TokenErrorResponse(final ErrorObject error) { 124 125 if (error == null) 126 throw new IllegalArgumentException("The error must not be null"); 127 128 this.error = error; 129 } 130 131 132 @Override 133 public boolean indicatesSuccess() { 134 135 return false; 136 } 137 138 139 @Override 140 public ErrorObject getErrorObject() { 141 142 return error; 143 } 144 145 146 /** 147 * Returns the JSON object for this token error response. 148 * 149 * @return The JSON object for this token error response. 150 */ 151 public JSONObject toJSONObject() { 152 153 JSONObject o = new JSONObject(); 154 155 // No error? 156 if (error == null) 157 return o; 158 159 o.put("error", error.getCode()); 160 161 if (error.getDescription() != null) 162 o.put("error_description", error.getDescription()); 163 164 if (error.getURI() != null) 165 o.put("error_uri", error.getURI().toString()); 166 167 return o; 168 } 169 170 171 @Override 172 public HTTPResponse toHTTPResponse() { 173 174 int statusCode = (error != null && error.getHTTPStatusCode() > 0) ? 175 error.getHTTPStatusCode() : HTTPResponse.SC_BAD_REQUEST; 176 177 HTTPResponse httpResponse = new HTTPResponse(statusCode); 178 179 if (error == null) 180 return httpResponse; 181 182 httpResponse.setContentType(CommonContentTypes.APPLICATION_JSON); 183 httpResponse.setCacheControl("no-store"); 184 httpResponse.setPragma("no-cache"); 185 186 httpResponse.setContent(toJSONObject().toString()); 187 188 return httpResponse; 189 } 190 191 192 /** 193 * Parses an OAuth 2.0 Token Error response from the specified JSON 194 * object. 195 * 196 * @param jsonObject The JSON object to parse. Its status code must not 197 * be 200 (OK). Must not be {@code null}. 198 * 199 * @return The token error response. 200 * 201 * @throws ParseException If the JSON object couldn't be parsed to an 202 * OAuth 2.0 Token Error response. 203 */ 204 public static TokenErrorResponse parse(final JSONObject jsonObject) 205 throws ParseException { 206 207 // No error code? 208 if (! jsonObject.containsKey("error")) 209 return new TokenErrorResponse(); 210 211 ErrorObject error; 212 213 try { 214 // Parse code 215 String code = JSONObjectUtils.getString(jsonObject, "error"); 216 String description = JSONObjectUtils.getString(jsonObject, "error_description", null); 217 URI uri = JSONObjectUtils.getURI(jsonObject, "error_uri", null); 218 219 error = new ErrorObject(code, description, HTTPResponse.SC_BAD_REQUEST, uri); 220 221 } catch (ParseException e) { 222 throw new ParseException("Missing or invalid token error response parameter: " + e.getMessage(), e); 223 } 224 225 return new TokenErrorResponse(error); 226 } 227 228 229 /** 230 * Parses an OAuth 2.0 Token Error response from the specified HTTP 231 * response. 232 * 233 * @param httpResponse The HTTP response to parse. Its status code must 234 * not be 200 (OK). Must not be {@code null}. 235 * 236 * @return The token error response. 237 * 238 * @throws ParseException If the HTTP response couldn't be parsed to an 239 * OAuth 2.0 Token Error response. 240 */ 241 public static TokenErrorResponse parse(final HTTPResponse httpResponse) 242 throws ParseException { 243 244 httpResponse.ensureStatusCodeNotOK(); 245 return new TokenErrorResponse(ErrorObject.parse(httpResponse)); 246 } 247}