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