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.openid.connect.sdk; 019 020 021import java.net.URI; 022import java.util.*; 023 024import com.nimbusds.jwt.JWT; 025import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse; 026import com.nimbusds.oauth2.sdk.ErrorObject; 027import com.nimbusds.oauth2.sdk.ParseException; 028import com.nimbusds.oauth2.sdk.ResponseMode; 029import com.nimbusds.oauth2.sdk.http.HTTPRequest; 030import com.nimbusds.oauth2.sdk.http.HTTPResponse; 031import com.nimbusds.oauth2.sdk.id.State; 032import net.jcip.annotations.Immutable; 033 034 035/** 036 * OpenID Connect authentication error response. Intended only for errors which 037 * are allowed to be communicated back to the requesting OAuth 2.0 client, such 038 * as {@code access_denied}. For a complete list see OAuth 2.0 (RFC 6749), 039 * sections 4.1.2.1 and 4.2.2.1, OpenID Connect Core 1.0 section 3.1.2.6. 040 * 041 * <p>If the authorisation request fails due to a missing, invalid, or 042 * mismatching {@code redirect_uri}, or if the {@code client_id} is missing or 043 * invalid, a response <strong>must not</strong> be sent back to the requesting 044 * client. Instead, the OpenID provider should simply display the error to the 045 * end-user. 046 * 047 * <p>Standard errors: 048 * 049 * <ul> 050 * <li>OAuth 2.0 authorisation errors: 051 * <ul> 052 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#INVALID_REQUEST} 053 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#UNAUTHORIZED_CLIENT} 054 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#ACCESS_DENIED} 055 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#UNSUPPORTED_RESPONSE_TYPE} 056 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#INVALID_SCOPE} 057 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#SERVER_ERROR} 058 * <li>{@link com.nimbusds.oauth2.sdk.OAuth2Error#TEMPORARILY_UNAVAILABLE} 059 * </ul> 060 * <li>OpenID Connect specific errors: 061 * <ul> 062 * <li>{@link OIDCError#INTERACTION_REQUIRED} 063 * <li>{@link OIDCError#LOGIN_REQUIRED} 064 * <li>{@link OIDCError#ACCOUNT_SELECTION_REQUIRED} 065 * <li>{@link OIDCError#CONSENT_REQUIRED} 066 * <li>{@link OIDCError#INVALID_REQUEST_URI} 067 * <li>{@link OIDCError#INVALID_REQUEST_OBJECT} 068 * <li>{@link OIDCError#REGISTRATION_NOT_SUPPORTED} 069 * <li>{@link OIDCError#REQUEST_NOT_SUPPORTED} 070 * <li>{@link OIDCError#REQUEST_URI_NOT_SUPPORTED} 071 * </ul> 072 * </li> 073 * </ul> 074 * 075 * <p>Example HTTP response: 076 * 077 * <pre> 078 * HTTP/1.1 302 Found 079 * Location: https://client.example.org/cb? 080 * error=invalid_request 081 * &error_description=the%20request%20is%20not%20valid%20or%20malformed 082 * &state=af0ifjsldkj 083 * </pre> 084 * 085 * <p>Related specifications: 086 * 087 * <ul> 088 * <li>OpenID Connect Core 1.0, section 3.1.2.6. 089 * <li>OAuth 2.0 (RFC 6749), sections 4.1.2.1 and 4.2.2.1. 090 * <li>OAuth 2.0 Multiple Response Type Encoding Practices 1.0. 091 * <li>OAuth 2.0 Form Post Response Mode 1.0. 092 * <li>Financial-grade API: JWT Secured Authorization Response Mode for 093 * OAuth 2.0 (JARM). 094 * </ul> 095 */ 096@Immutable 097public class AuthenticationErrorResponse 098 extends AuthorizationErrorResponse 099 implements AuthenticationResponse { 100 101 102 /** 103 * The standard errors for an OpenID Connect authentication error 104 * response. 105 */ 106 private static final Set<ErrorObject> stdErrors = new HashSet<>(); 107 108 109 static { 110 stdErrors.addAll(AuthorizationErrorResponse.getStandardErrors()); 111 112 stdErrors.add(OIDCError.INTERACTION_REQUIRED); 113 stdErrors.add(OIDCError.LOGIN_REQUIRED); 114 stdErrors.add(OIDCError.ACCOUNT_SELECTION_REQUIRED); 115 stdErrors.add(OIDCError.CONSENT_REQUIRED); 116 stdErrors.add(OIDCError.INVALID_REQUEST_URI); 117 stdErrors.add(OIDCError.INVALID_REQUEST_OBJECT); 118 stdErrors.add(OIDCError.REGISTRATION_NOT_SUPPORTED); 119 stdErrors.add(OIDCError.REQUEST_NOT_SUPPORTED); 120 stdErrors.add(OIDCError.REQUEST_URI_NOT_SUPPORTED); 121 } 122 123 124 /** 125 * Gets the standard errors for an OpenID Connect authentication error 126 * response. 127 * 128 * @return The standard errors, as a read-only set. 129 */ 130 public static Set<ErrorObject> getStandardErrors() { 131 132 return Collections.unmodifiableSet(stdErrors); 133 } 134 135 136 /** 137 * Creates a new OpenID Connect authentication error response. 138 * 139 * @param redirectURI The base redirection URI. Must not be 140 * {@code null}. 141 * @param error The error. Should match one of the 142 * {@link #getStandardErrors standard errors} for an 143 * OpenID Connect authentication error response. 144 * Must not be {@code null}. 145 * @param state The state, {@code null} if not requested. 146 * @param rm The implied response mode, {@code null} if 147 * unknown. 148 */ 149 public AuthenticationErrorResponse(final URI redirectURI, 150 final ErrorObject error, 151 final State state, 152 final ResponseMode rm) { 153 154 super(redirectURI, error, state, rm); 155 } 156 157 158 /** 159 * Creates a new JSON Web Token (JWT) secured OpenID Connect 160 * authentication error response. 161 * 162 * @param redirectURI The base redirection URI. Must not be 163 * {@code null}. 164 * @param jwtResponse The JWT-secured response. Must not be 165 * {@code null}. 166 * @param rm The implied response mode, {@code null} if 167 * unknown. 168 */ 169 public AuthenticationErrorResponse(final URI redirectURI, 170 final JWT jwtResponse, 171 final ResponseMode rm) { 172 173 super(redirectURI, jwtResponse, rm); 174 } 175 176 177 @Override 178 public AuthenticationSuccessResponse toSuccessResponse() { 179 throw new ClassCastException("Cannot cast to AuthenticationSuccessResponse"); 180 } 181 182 183 @Override 184 public AuthenticationErrorResponse toErrorResponse() { 185 return this; 186 } 187 188 189 /** 190 * Converts the specified general OAuth 2.0 authorisation error 191 * response instance to an OpenID authentication error instance. 192 * 193 * @param errorResponse The OAuth 2.0 authorisation error response. 194 * Must not be {@code null}. 195 * 196 * @return The OpenID authentication error instance. 197 */ 198 private static AuthenticationErrorResponse toAuthenticationErrorResponse(final AuthorizationErrorResponse errorResponse) { 199 200 if (errorResponse.getJWTResponse() != null) { 201 // JARM 202 return new AuthenticationErrorResponse( 203 errorResponse.getRedirectionURI(), 204 errorResponse.getJWTResponse(), 205 errorResponse.getResponseMode()); 206 } 207 208 return new AuthenticationErrorResponse( 209 errorResponse.getRedirectionURI(), 210 errorResponse.getErrorObject(), 211 errorResponse.getState(), 212 errorResponse.getResponseMode()); 213 } 214 215 216 /** 217 * Parses an OpenID Connect authentication error response. 218 * 219 * @param redirectURI The base redirection URI. Must not be 220 * {@code null}. 221 * @param params The response parameters to parse. Must not be 222 * {@code null}. 223 * 224 * @return The OpenID Connect authentication error response. 225 * 226 * @throws ParseException If the parameters couldn't be parsed to an 227 * OpenID Connect authentication error response. 228 */ 229 public static AuthenticationErrorResponse parse(final URI redirectURI, 230 final Map<String, List<String>> params) 231 throws ParseException { 232 233 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(redirectURI, params)); 234 } 235 236 237 /** 238 * Parses an OpenID Connect authentication error response. 239 * 240 * <p>Use a relative URI if the host, port and path details are not 241 * known: 242 * 243 * <pre> 244 * URI relUrl = new URI("https:///?error=invalid_request"); 245 * </pre> 246 * 247 * <p>Example URI: 248 * 249 * <pre> 250 * https://client.example.com/cb? 251 * error=invalid_request 252 * &error_description=the%20request%20is%20not%20valid%20or%20malformed 253 * &state=af0ifjsldkj 254 * </pre> 255 * 256 * @param uri The URI to parse. Can be absolute or relative, with a 257 * fragment or query string containing the authorisation 258 * response parameters. Must not be {@code null}. 259 * 260 * @return The OpenID Connect authentication error response. 261 * 262 * @throws ParseException If the URI couldn't be parsed to an OpenID 263 * Connect authentication error response. 264 */ 265 public static AuthenticationErrorResponse parse(final URI uri) 266 throws ParseException { 267 268 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(uri)); 269 } 270 271 272 /** 273 * Parses an OpenID Connect authentication error response from the 274 * specified initial HTTP 302 redirect response generated at the 275 * authorisation endpoint. 276 * 277 * <p>Example HTTP response: 278 * 279 * <pre> 280 * HTTP/1.1 302 Found 281 * Location: https://client.example.com/cb?error=invalid_request&state=af0ifjsldkj 282 * </pre> 283 * 284 * @param httpResponse The HTTP response to parse. Must not be 285 * {@code null}. 286 * 287 * @return The OpenID Connect authentication error response. 288 * 289 * @throws ParseException If the HTTP response couldn't be parsed to an 290 * OpenID Connect authentication error response. 291 */ 292 public static AuthenticationErrorResponse parse(final HTTPResponse httpResponse) 293 throws ParseException { 294 295 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(httpResponse)); 296 } 297 298 299 /** 300 * Parses an OpenID Connect authentication error response from the 301 * specified HTTP request at the client redirection (callback) URI. 302 * Applies to {@code query}, {@code fragment} and {@code form_post} 303 * response modes. 304 * 305 * <p>Example HTTP request (authorisation success): 306 * 307 * <pre> 308 * GET /cb?error=invalid_request&state=af0ifjsldkj HTTP/1.1 309 * Host: client.example.com 310 * </pre> 311 * 312 * @see #parse(HTTPResponse) 313 * 314 * @param httpRequest The HTTP request to parse. Must not be 315 * {@code null}. 316 * 317 * @return The authentication error response. 318 * 319 * @throws ParseException If the HTTP request couldn't be parsed to an 320 * OpenID Connect authentication error response. 321 */ 322 public static AuthenticationErrorResponse parse(final HTTPRequest httpRequest) 323 throws ParseException { 324 325 return parse(httpRequest.getURI(), parseResponseParameters(httpRequest)); 326 } 327}