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 net.jcip.annotations.Immutable; 025 026import com.nimbusds.jwt.JWT; 027import com.nimbusds.oauth2.sdk.*; 028import com.nimbusds.oauth2.sdk.http.HTTPRequest; 029import com.nimbusds.oauth2.sdk.http.HTTPResponse; 030import com.nimbusds.oauth2.sdk.id.Issuer; 031import com.nimbusds.oauth2.sdk.id.State; 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, section 3.1.2.6. 088 * <li>OpenID Connect Core Unmet Authentication Requirements 1.0. 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 * <li>OAuth 2.0 Authorization Server Issuer Identifier in Authorization 095 * Response (draft-ietf-oauth-iss-auth-resp-00). 096 * </ul> 097 */ 098@Immutable 099public class AuthenticationErrorResponse 100 extends AuthorizationErrorResponse 101 implements AuthenticationResponse { 102 103 104 /** 105 * The standard errors for an OpenID Connect authentication error 106 * response. 107 */ 108 private static final Set<ErrorObject> stdErrors = new HashSet<>(); 109 110 111 static { 112 stdErrors.addAll(AuthorizationErrorResponse.getStandardErrors()); 113 114 stdErrors.add(OIDCError.INTERACTION_REQUIRED); 115 stdErrors.add(OIDCError.LOGIN_REQUIRED); 116 stdErrors.add(OIDCError.ACCOUNT_SELECTION_REQUIRED); 117 stdErrors.add(OIDCError.CONSENT_REQUIRED); 118 stdErrors.add(OIDCError.UNMET_AUTHENTICATION_REQUIREMENTS); 119 stdErrors.add(OIDCError.REGISTRATION_NOT_SUPPORTED); 120 } 121 122 123 /** 124 * Gets the standard errors for an OpenID Connect authentication error 125 * response. 126 * 127 * @return The standard errors, as a read-only set. 128 */ 129 public static Set<ErrorObject> getStandardErrors() { 130 131 return Collections.unmodifiableSet(stdErrors); 132 } 133 134 135 /** 136 * Creates a new OpenID Connect authentication error response. 137 * 138 * @param redirectURI The base redirection URI. Must not be 139 * {@code null}. 140 * @param error The error. Should match one of the 141 * {@link #getStandardErrors standard errors} for an 142 * OpenID Connect authentication error response. 143 * Must not be {@code null}. 144 * @param state The state, {@code null} if not requested. 145 * @param rm The implied response mode, {@code null} if 146 * unknown. 147 */ 148 public AuthenticationErrorResponse(final URI redirectURI, 149 final ErrorObject error, 150 final State state, 151 final ResponseMode rm) { 152 153 this(redirectURI, error, state, null, rm); 154 } 155 156 157 /** 158 * Creates a new OpenID Connect authentication error response. 159 * 160 * @param redirectURI The base redirection URI. Must not be 161 * {@code null}. 162 * @param error The error. Should match one of the 163 * {@link #getStandardErrors standard errors} for an 164 * OpenID Connect authentication error response. 165 * Must not be {@code null}. 166 * @param state The state, {@code null} if not requested. 167 * @param issuer The issuer, {@code null} if not specified. 168 * @param rm The implied response mode, {@code null} if 169 * unknown. 170 */ 171 public AuthenticationErrorResponse(final URI redirectURI, 172 final ErrorObject error, 173 final State state, 174 final Issuer issuer, 175 final ResponseMode rm) { 176 177 super(redirectURI, error, state, issuer, rm); 178 } 179 180 181 /** 182 * Creates a new JSON Web Token (JWT) secured OpenID Connect 183 * authentication error response. 184 * 185 * @param redirectURI The base redirection URI. Must not be 186 * {@code null}. 187 * @param jwtResponse The JWT-secured response. Must not be 188 * {@code null}. 189 * @param rm The implied response mode, {@code null} if 190 * unknown. 191 */ 192 public AuthenticationErrorResponse(final URI redirectURI, 193 final JWT jwtResponse, 194 final ResponseMode rm) { 195 196 super(redirectURI, jwtResponse, rm); 197 } 198 199 200 @Override 201 public AuthenticationSuccessResponse toSuccessResponse() { 202 throw new ClassCastException("Cannot cast to AuthenticationSuccessResponse"); 203 } 204 205 206 @Override 207 public AuthenticationErrorResponse toErrorResponse() { 208 return this; 209 } 210 211 212 /** 213 * Converts the specified general OAuth 2.0 authorisation error 214 * response instance to an OpenID authentication error instance. 215 * 216 * @param errorResponse The OAuth 2.0 authorisation error response. 217 * Must not be {@code null}. 218 * 219 * @return The OpenID authentication error instance. 220 */ 221 private static AuthenticationErrorResponse toAuthenticationErrorResponse(final AuthorizationErrorResponse errorResponse) { 222 223 if (errorResponse.getJWTResponse() != null) { 224 // JARM 225 return new AuthenticationErrorResponse( 226 errorResponse.getRedirectionURI(), 227 errorResponse.getJWTResponse(), 228 errorResponse.getResponseMode()); 229 } 230 231 return new AuthenticationErrorResponse( 232 errorResponse.getRedirectionURI(), 233 errorResponse.getErrorObject(), 234 errorResponse.getState(), 235 errorResponse.getIssuer(), 236 errorResponse.getResponseMode()); 237 } 238 239 240 /** 241 * Parses an OpenID Connect authentication error response. 242 * 243 * @param redirectURI The base redirection URI. Must not be 244 * {@code null}. 245 * @param params The response parameters to parse. Must not be 246 * {@code null}. 247 * 248 * @return The OpenID Connect authentication error response. 249 * 250 * @throws ParseException If the parameters couldn't be parsed to an 251 * OpenID Connect authentication error response. 252 */ 253 public static AuthenticationErrorResponse parse(final URI redirectURI, 254 final Map<String, List<String>> params) 255 throws ParseException { 256 257 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(redirectURI, params)); 258 } 259 260 261 /** 262 * Parses an OpenID Connect authentication error response. 263 * 264 * <p>Use a relative URI if the host, port and path details are not 265 * known: 266 * 267 * <pre> 268 * URI relUrl = new URI("https:///?error=invalid_request"); 269 * </pre> 270 * 271 * <p>Example URI: 272 * 273 * <pre> 274 * https://client.example.com/cb? 275 * error=invalid_request 276 * &error_description=the%20request%20is%20not%20valid%20or%20malformed 277 * &state=af0ifjsldkj 278 * </pre> 279 * 280 * @param uri The URI to parse. Can be absolute or relative, with a 281 * fragment or query string containing the authorisation 282 * response parameters. Must not be {@code null}. 283 * 284 * @return The OpenID Connect authentication error response. 285 * 286 * @throws ParseException If the URI couldn't be parsed to an OpenID 287 * Connect authentication error response. 288 */ 289 public static AuthenticationErrorResponse parse(final URI uri) 290 throws ParseException { 291 292 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(uri)); 293 } 294 295 296 /** 297 * Parses an OpenID Connect authentication error response from the 298 * specified initial HTTP 302 redirect response generated at the 299 * authorisation endpoint. 300 * 301 * <p>Example HTTP response: 302 * 303 * <pre> 304 * HTTP/1.1 302 Found 305 * Location: https://client.example.com/cb?error=invalid_request&state=af0ifjsldkj 306 * </pre> 307 * 308 * @param httpResponse The HTTP response to parse. Must not be 309 * {@code null}. 310 * 311 * @return The OpenID Connect authentication error response. 312 * 313 * @throws ParseException If the HTTP response couldn't be parsed to an 314 * OpenID Connect authentication error response. 315 */ 316 public static AuthenticationErrorResponse parse(final HTTPResponse httpResponse) 317 throws ParseException { 318 319 return toAuthenticationErrorResponse(AuthorizationErrorResponse.parse(httpResponse)); 320 } 321 322 323 /** 324 * Parses an OpenID Connect authentication error response from the 325 * specified HTTP request at the client redirection (callback) URI. 326 * Applies to {@code query}, {@code fragment} and {@code form_post} 327 * response modes. 328 * 329 * <p>Example HTTP request (authorisation success): 330 * 331 * <pre> 332 * GET /cb?error=invalid_request&state=af0ifjsldkj HTTP/1.1 333 * Host: client.example.com 334 * </pre> 335 * 336 * @see #parse(HTTPResponse) 337 * 338 * @param httpRequest The HTTP request to parse. Must not be 339 * {@code null}. 340 * 341 * @return The authentication error response. 342 * 343 * @throws ParseException If the HTTP request couldn't be parsed to an 344 * OpenID Connect authentication error response. 345 */ 346 public static AuthenticationErrorResponse parse(final HTTPRequest httpRequest) 347 throws ParseException { 348 349 return parse(httpRequest.getURI(), parseResponseParameters(httpRequest)); 350 } 351}