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