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