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 *           &amp;error_description=the%20request%20is%20not%20valid%20or%20malformed
076 *           &amp;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         * &amp;error_description=the%20request%20is%20not%20valid%20or%20malformed
193         * &amp;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&amp;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&amp;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}