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