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