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