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