001package com.nimbusds.openid.connect.sdk;
002
003
004import java.net.URI;
005import java.net.URISyntaxException;
006import java.util.*;
007
008import com.nimbusds.jwt.JWT;
009import com.nimbusds.jwt.JWTParser;
010import com.nimbusds.langtag.LangTag;
011import com.nimbusds.langtag.LangTagException;
012import com.nimbusds.oauth2.sdk.*;
013import com.nimbusds.oauth2.sdk.http.HTTPRequest;
014import com.nimbusds.oauth2.sdk.id.ClientID;
015import com.nimbusds.oauth2.sdk.id.State;
016import com.nimbusds.oauth2.sdk.pkce.CodeChallenge;
017import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod;
018import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
019import com.nimbusds.oauth2.sdk.util.URIUtils;
020import com.nimbusds.oauth2.sdk.util.URLUtils;
021import com.nimbusds.openid.connect.sdk.claims.ACR;
022import net.jcip.annotations.Immutable;
023import net.minidev.json.JSONObject;
024import org.apache.commons.lang3.StringUtils;
025
026
027/**
028 * OpenID Connect authentication request. Intended to authenticate an end-user
029 * and request the end-user's authorisation to release information to the
030 * client. Supports custom request parameters.
031 *
032 * <p>Example HTTP request (code flow):
033 *
034 * <pre>
035 * https://server.example.com/op/authorize?
036 * response_type=code%20id_token
037 * &amp;client_id=s6BhdRkqt3
038 * &amp;redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
039 * &amp;scope=openid
040 * &amp;nonce=n-0S6_WzA2Mj
041 * &amp;state=af0ifjsldkj
042 * </pre>
043 *
044 * <p>Related specifications:
045 *
046 * <ul>
047 *     <li>OpenID Connect Core 1.0, section 3.1.2.1.
048 *     <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636).
049 * </ul>
050 */
051@Immutable
052public class AuthenticationRequest extends AuthorizationRequest {
053
054
055        /**
056         * The registered parameter names.
057         */
058        private static final Set<String> REGISTERED_PARAMETER_NAMES;
059
060
061        /**
062         * Initialises the registered parameter name set.
063         */
064        static {
065                Set<String> p = new HashSet<>();
066
067                p.addAll(AuthorizationRequest.getRegisteredParameterNames());
068
069                p.add("nonce");
070                p.add("display");
071                p.add("prompt");
072                p.add("max_age");
073                p.add("ui_locales");
074                p.add("claims_locales");
075                p.add("id_token_hint");
076                p.add("login_hint");
077                p.add("acr_values");
078                p.add("claims");
079                p.add("request_uri");
080                p.add("request");
081
082                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
083        }
084        
085        
086        /**
087         * The nonce (required for implicit flow, optional for code flow).
088         */
089        private final Nonce nonce;
090        
091        
092        /**
093         * The requested display type (optional).
094         */
095        private final Display display;
096        
097        
098        /**
099         * The requested prompt (optional).
100         */
101        private final Prompt prompt;
102
103
104        /**
105         * The required maximum authentication age, in seconds, -1 if not
106         * specified, zero implies prompt=login (optional).
107         */
108        private final int maxAge;
109
110
111        /**
112         * The end-user's preferred languages and scripts for the user 
113         * interface (optional).
114         */
115        private final List<LangTag> uiLocales;
116
117
118        /**
119         * The end-user's preferred languages and scripts for claims being 
120         * returned (optional).
121         */
122        private final List<LangTag> claimsLocales;
123
124
125        /**
126         * Previously issued ID Token passed to the authorisation server as a 
127         * hint about the end-user's current or past authenticated session with
128         * the client (optional). Should be present when {@code prompt=none} is 
129         * used.
130         */
131        private final JWT idTokenHint;
132
133
134        /**
135         * Hint to the authorisation server about the login identifier the 
136         * end-user may use to log in (optional).
137         */
138        private final String loginHint;
139
140
141        /**
142         * Requested Authentication Context Class Reference values (optional).
143         */
144        private final List<ACR> acrValues;
145
146
147        /**
148         * Individual claims to be returned (optional).
149         */
150        private final ClaimsRequest claims;
151        
152        
153        /**
154         * Request object (optional).
155         */
156        private final JWT requestObject;
157        
158        
159        /**
160         * Request object URI (optional).
161         */
162        private final URI requestURI;
163
164
165        /**
166         * Builder for constructing OpenID Connect authentication requests.
167         */
168        public static class Builder {
169
170
171                /**
172                 * The endpoint URI (optional).
173                 */
174                private URI uri;
175
176
177                /**
178                 * The response type (required).
179                 */
180                private final ResponseType rt;
181
182
183                /**
184                 * The client identifier (required).
185                 */
186                private final ClientID clientID;
187
188
189                /**
190                 * The redirection URI where the response will be sent
191                 * (required).
192                 */
193                private final URI redirectURI;
194
195
196                /**
197                 * The scope (required).
198                 */
199                private final Scope scope;
200
201
202                /**
203                 * The opaque value to maintain state between the request and
204                 * the callback (recommended).
205                 */
206                private State state;
207
208
209                /**
210                 * The nonce (required for implicit flow, optional for code
211                 * flow).
212                 */
213                private Nonce nonce;
214
215
216                /**
217                 * The requested display type (optional).
218                 */
219                private Display display;
220
221
222                /**
223                 * The requested prompt (optional).
224                 */
225                private Prompt prompt;
226
227
228                /**
229                 * The required maximum authentication age, in seconds, -1 if
230                 * not specified, zero implies prompt=login (optional).
231                 */
232                private int maxAge = -1;
233
234
235                /**
236                 * The end-user's preferred languages and scripts for the user
237                 * interface (optional).
238                 */
239                private List<LangTag> uiLocales;
240
241
242                /**
243                 * The end-user's preferred languages and scripts for claims
244                 * being returned (optional).
245                 */
246                private List<LangTag> claimsLocales;
247
248
249                /**
250                 * Previously issued ID Token passed to the authorisation
251                 * server as a hint about the end-user's current or past
252                 * authenticated session with the client (optional). Should be
253                 * present when {@code prompt=none} is used.
254                 */
255                private JWT idTokenHint;
256
257
258                /**
259                 * Hint to the authorisation server about the login identifier
260                 * the end-user may use to log in (optional).
261                 */
262                private String loginHint;
263
264
265                /**
266                 * Requested Authentication Context Class Reference values
267                 * (optional).
268                 */
269                private List<ACR> acrValues;
270
271
272                /**
273                 * Individual claims to be returned (optional).
274                 */
275                private ClaimsRequest claims;
276
277
278                /**
279                 * Request object (optional).
280                 */
281                private JWT requestObject;
282
283
284                /**
285                 * Request object URI (optional).
286                 */
287                private URI requestURI;
288
289
290                /**
291                 * The response mode (optional).
292                 */
293                private ResponseMode rm;
294
295
296                /**
297                 * The authorisation code challenge for PKCE (optional).
298                 */
299                private CodeChallenge codeChallenge;
300
301
302                /**
303                 * The authorisation code challenge method for PKCE (optional).
304                 */
305                private CodeChallengeMethod codeChallengeMethod;
306
307
308                /**
309                 * The additional custom parameters.
310                 */
311                private Map<String,String> customParams = new HashMap<>();
312
313
314                /**
315                 * Creates a new OpenID Connect authentication request builder.
316                 *
317                 * @param rt          The response type. Corresponds to the
318                 *                    {@code response_type} parameter. Must
319                 *                    specify a valid OpenID Connect response
320                 *                    type. Must not be {@code null}.
321                 * @param scope       The request scope. Corresponds to the
322                 *                    {@code scope} parameter. Must contain an
323                 *                    {@link OIDCScopeValue#OPENID openid
324                 *                    value}. Must not be {@code null}.
325                 * @param clientID    The client identifier. Corresponds to the
326                 *                    {@code client_id} parameter. Must not be
327                 *                    {@code null}.
328                 * @param redirectURI The redirection URI. Corresponds to the
329                 *                    {@code redirect_uri} parameter. Must not
330                 *                    be {@code null} unless set by means of
331                 *                    the optional {@code request_object} /
332                 *                    {@code request_uri} parameter.
333                 */
334                public Builder(final ResponseType rt,
335                               final Scope scope,
336                               final ClientID clientID,
337                               final URI redirectURI) {
338
339                        if (rt == null)
340                                throw new IllegalArgumentException("The response type must not be null");
341
342                        OIDCResponseTypeValidator.validate(rt);
343
344                        this.rt = rt;
345
346                        if (scope == null)
347                                throw new IllegalArgumentException("The scope must not be null");
348
349                        if (! scope.contains(OIDCScopeValue.OPENID))
350                                throw new IllegalArgumentException("The scope must include an \"openid\" value");
351
352                        this.scope = scope;
353
354                        if (clientID == null)
355                                throw new IllegalArgumentException("The client ID must not be null");
356
357                        this.clientID = clientID;
358
359                        // Check presence at build time
360                        this.redirectURI = redirectURI;
361                }
362
363
364                /**
365                 * Sets the state. Corresponds to the recommended {@code state}
366                 * parameter.
367                 *
368                 * @param state The state, {@code null} if not specified.
369                 *
370                 * @return This builder.
371                 */
372                public Builder state(final State state) {
373
374                        this.state = state;
375                        return this;
376                }
377
378
379                /**
380                 * Sets the URI of the endpoint (HTTP or HTTPS) for which the
381                 * request is intended.
382                 *
383                 * @param uri The endpoint URI, {@code null} if not specified.
384                 *
385                 * @return This builder.
386                 */
387                public Builder endpointURI(final URI uri) {
388
389                        this.uri = uri;
390                        return this;
391                }
392
393
394                /**
395                 * Sets the nonce. Corresponds to the conditionally optional
396                 * {@code nonce} parameter.
397                 *
398                 * @param nonce The nonce, {@code null} if not specified.
399                 */
400                public Builder nonce(final Nonce nonce) {
401
402                        this.nonce = nonce;
403                        return this;
404                }
405
406
407                /**
408                 * Sets the requested display type. Corresponds to the optional
409                 * {@code display} parameter.
410                 *
411                 * @param display The requested display type, {@code null} if
412                 *                not specified.
413                 */
414                public Builder display(final Display display) {
415
416                        this.display = display;
417                        return this;
418                }
419
420
421                /**
422                 * Sets the requested prompt. Corresponds to the optional
423                 * {@code prompt} parameter.
424                 *
425                 * @param prompt The requested prompt, {@code null} if not
426                 *               specified.
427                 */
428                public Builder prompt(final Prompt prompt) {
429
430                        this.prompt = prompt;
431                        return this;
432                }
433
434
435                /**
436                 * Sets the required maximum authentication age. Corresponds to
437                 * the optional {@code max_age} parameter.
438                 *
439                 * @param maxAge The maximum authentication age, in seconds; 0
440                 *               if not specified.
441                 */
442                public Builder maxAge(final int maxAge) {
443
444                        this.maxAge = maxAge;
445                        return this;
446                }
447
448
449                /**
450                 * Sets the end-user's preferred languages and scripts for the
451                 * user interface, ordered by preference. Corresponds to the
452                 * optional {@code ui_locales} parameter.
453                 *
454                 * @param uiLocales The preferred UI locales, {@code null} if
455                 *                  not specified.
456                 */
457                public Builder uiLocales(final List<LangTag> uiLocales) {
458
459                        this.uiLocales = uiLocales;
460                        return this;
461                }
462
463
464                /**
465                 * Sets the end-user's preferred languages and scripts for the
466                 * claims being returned, ordered by preference. Corresponds to
467                 * the optional {@code claims_locales} parameter.
468                 *
469                 * @param claimsLocales The preferred claims locales,
470                 *                      {@code null} if not specified.
471                 */
472                public Builder claimsLocales(final List<LangTag> claimsLocales) {
473
474                        this.claimsLocales = claimsLocales;
475                        return this;
476                }
477
478
479                /**
480                 * Sets the ID Token hint. Corresponds to the conditionally
481                 * optional {@code id_token_hint} parameter.
482                 *
483                 * @param idTokenHint The ID Token hint, {@code null} if not
484                 *                    specified.
485                 */
486                public Builder idTokenHint(final JWT idTokenHint) {
487
488                        this.idTokenHint = idTokenHint;
489                        return this;
490                }
491
492
493                /**
494                 * Sets the login hint. Corresponds to the optional
495                 * {@code login_hint} parameter.
496                 *
497                 * @param loginHint The login hint, {@code null} if not
498                 *                  specified.
499                 */
500                public Builder loginHint(final String loginHint) {
501
502                        this.loginHint = loginHint;
503                        return this;
504                }
505
506
507                /**
508                 * Sets the requested Authentication Context Class Reference
509                 * values. Corresponds to the optional {@code acr_values}
510                 * parameter.
511                 *
512                 * @param acrValues The requested ACR values, {@code null} if
513                 *                  not specified.
514                 */
515                public Builder acrValues(final List<ACR> acrValues) {
516
517                        this.acrValues = acrValues;
518                        return this;
519                }
520
521
522                /**
523                 * Sets the individual claims to be returned. Corresponds to
524                 * the optional {@code claims} parameter.
525                 *
526                 * @param claims The individual claims to be returned,
527                 *               {@code null} if not specified.
528                 */
529                public Builder claims(final ClaimsRequest claims) {
530
531                        this.claims = claims;
532                        return this;
533                }
534
535
536                /**
537                 * Sets the request object. Corresponds to the optional
538                 * {@code request} parameter. Must not be specified together
539                 * with a request object URI.
540                 *
541                 * @return The request object, {@code null} if not specified.
542                 */
543                public Builder requestObject(final JWT requestObject) {
544
545                        this.requestObject = requestObject;
546                        return this;
547                }
548
549
550                /**
551                 * Sets the request object URI. Corresponds to the optional
552                 * {@code request_uri} parameter. Must not be specified
553                 * together with a request object.
554                 *
555                 * @param requestURI The request object URI, {@code null} if
556                 *                   not specified.
557                 */
558                public Builder requestURI(final URI requestURI) {
559
560                        this.requestURI = requestURI;
561                        return this;
562                }
563
564
565                /**
566                 * Sets the response mode. Corresponds to the optional
567                 * {@code response_mode} parameter. Use of this parameter is
568                 * not recommended unless a non-default response mode is
569                 * requested (e.g. form_post).
570                 *
571                 * @param rm The response mode, {@code null} if not specified.
572                 *
573                 * @return This builder.
574                 */
575                public Builder responseMode(final ResponseMode rm) {
576
577                        this.rm = rm;
578                        return this;
579                }
580
581
582                /**
583                 * Sets the code challenge for Proof Key for Code Exchange
584                 * (PKCE) by public OAuth clients.
585                 *
586                 * @param codeChallenge       The code challenge, {@code null}
587                 *                            if not specified.
588                 * @param codeChallengeMethod The code challenge method,
589                 *                            {@code null} if not specified.
590                 *
591                 * @return This builder.
592                 */
593                public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) {
594
595                        this.codeChallenge = codeChallenge;
596                        this.codeChallengeMethod = codeChallengeMethod;
597                        return this;
598                }
599
600
601                /**
602                 * Sets the specified additional custom parameter.
603                 *
604                 * @param name  The parameter name. Must not be {@code null}.
605                 * @param value The parameter value, {@code null} if not
606                 *              specified.
607                 *
608                 * @return This builder.
609                 */
610                public Builder customParameter(final String name, final String value) {
611
612                        customParams.put(name, value);
613                        return this;
614                }
615
616
617                /**
618                 * Builds a new authentication request.
619                 *
620                 * @return The authentication request.
621                 */
622                public AuthenticationRequest build() {
623
624                        try {
625                                return new AuthenticationRequest(
626                                        uri, rt, rm, scope, clientID, redirectURI, state, nonce,
627                                        display, prompt, maxAge, uiLocales, claimsLocales,
628                                        idTokenHint, loginHint, acrValues, claims,
629                                        requestObject, requestURI,
630                                        codeChallenge, codeChallengeMethod,
631                                        customParams);
632
633                        } catch (IllegalArgumentException e) {
634
635                                throw new IllegalStateException(e.getMessage(), e);
636                        }
637                }
638        }
639        
640        
641        /**
642         * Creates a new minimal OpenID Connect authentication request.
643         *
644         * @param uri         The URI of the OAuth 2.0 authorisation endpoint.
645         *                    May be {@code null} if the {@link #toHTTPRequest}
646         *                    method will not be used.
647         * @param rt          The response type. Corresponds to the 
648         *                    {@code response_type} parameter. Must specify a
649         *                    valid OpenID Connect response type. Must not be
650         *                    {@code null}.
651         * @param scope       The request scope. Corresponds to the
652         *                    {@code scope} parameter. Must contain an
653         *                    {@link OIDCScopeValue#OPENID openid value}. Must
654         *                    not be {@code null}.
655         * @param clientID    The client identifier. Corresponds to the
656         *                    {@code client_id} parameter. Must not be 
657         *                    {@code null}.
658         * @param redirectURI The redirection URI. Corresponds to the
659         *                    {@code redirect_uri} parameter. Must not be 
660         *                    {@code null}.
661         * @param state       The state. Corresponds to the {@code state}
662         *                    parameter. May be {@code null}.
663         * @param nonce       The nonce. Corresponds to the {@code nonce} 
664         *                    parameter. May be {@code null} for code flow.
665         */
666        public AuthenticationRequest(final URI uri,
667                                     final ResponseType rt,
668                                     final Scope scope,
669                                     final ClientID clientID,
670                                     final URI redirectURI,
671                                     final State state,
672                                     final Nonce nonce) {
673
674                // Not specified: display, prompt, maxAge, uiLocales, claimsLocales, 
675                // idTokenHint, loginHint, acrValues, claims
676                // codeChallenge, codeChallengeMethod
677                this(uri, rt, null, scope, clientID, redirectURI, state, nonce,
678                     null, null, -1, null, null,
679                     null, null, null, null, null, null,
680                        null, null);
681        }
682        
683        
684        /**
685         * Creates a new OpenID Connect authentication request.
686         *
687         * @param uri                 The URI of the OAuth 2.0 authorisation
688         *                            endpoint. May be {@code null} if the
689         *                            {@link #toHTTPRequest} method will not be
690         *                            used.
691         * @param rt                  The response type set. Corresponds to the
692         *                            {@code response_type} parameter. Must
693         *                            specify a valid OpenID Connect response
694         *                            type. Must not be {@code null}.
695         * @param rm                  The response mode. Corresponds to the
696         *                            optional {@code response_mode} parameter.
697         *                            Use of this parameter is not recommended
698         *                            unless a non-default response mode is
699         *                            requested (e.g. form_post).
700         * @param scope               The request scope. Corresponds to the
701         *                            {@code scope} parameter. Must contain an
702         *                            {@link OIDCScopeValue#OPENID openid value}.
703         *                            Must not be {@code null}.
704         * @param clientID            The client identifier. Corresponds to the
705         *                            {@code client_id} parameter. Must not be
706         *                            {@code null}.
707         * @param redirectURI         The redirection URI. Corresponds to the
708         *                            {@code redirect_uri} parameter. Must not
709         *                            be {@code null} unless set by means of
710         *                            the optional {@code request_object} /
711         *                            {@code request_uri} parameter.
712         * @param state               The state. Corresponds to the recommended
713         *                            {@code state} parameter. {@code null} if
714         *                            not specified.
715         * @param nonce               The nonce. Corresponds to the
716         *                            {@code nonce} parameter. May be
717         *                            {@code null} for code flow.
718         * @param display             The requested display type. Corresponds
719         *                            to the optional {@code display}
720         *                            parameter.
721         *                            {@code null} if not specified.
722         * @param prompt              The requested prompt. Corresponds to the
723         *                            optional {@code prompt} parameter.
724         *                            {@code null} if not specified.
725         * @param maxAge              The required maximum authentication age,
726         *                            in seconds. Corresponds to the optional
727         *                            {@code max_age} parameter. Zero if not
728         *                            specified.
729         * @param uiLocales           The preferred languages and scripts for
730         *                            the user interface. Corresponds to the
731         *                            optional {@code ui_locales} parameter.
732         *                            {@code null} if not specified.
733         * @param claimsLocales       The preferred languages and scripts for
734         *                            claims being returned. Corresponds to the
735         *                            optional {@code claims_locales}
736         *                            parameter. {@code null} if not specified.
737         * @param idTokenHint         The ID Token hint. Corresponds to the
738         *                            optional {@code id_token_hint} parameter.
739         *                            {@code null} if not specified.
740         * @param loginHint           The login hint. Corresponds to the
741         *                            optional {@code login_hint} parameter.
742         *                            {@code null} if not specified.
743         * @param acrValues           The requested Authentication Context
744         *                            Class Reference values. Corresponds to
745         *                            the optional {@code acr_values}
746         *                            parameter. {@code null} if not specified.
747         * @param claims              The individual claims to be returned.
748         *                            Corresponds to the optional
749         *                            {@code claims} parameter. {@code null} if
750         *                            not specified.
751         * @param requestObject       The request object. Corresponds to the
752         *                            optional {@code request} parameter. Must
753         *                            not be specified together with a request
754         *                            object URI. {@code null} if not
755         *                            specified.
756         * @param requestURI          The request object URI. Corresponds to
757         *                            the optional {@code request_uri}
758         *                            parameter. Must not be specified together
759         *                            with a request object. {@code null} if
760         *                            not specified.
761         * @param codeChallenge       The code challenge for PKCE, {@code null}
762         *                            if not specified.
763         * @param codeChallengeMethod The code challenge method for PKCE,
764         *                            {@code null} if not specified.
765         */
766        public AuthenticationRequest(final URI uri,
767                                     final ResponseType rt,
768                                     final ResponseMode rm,
769                                     final Scope scope,
770                                     final ClientID clientID,
771                                     final URI redirectURI,
772                                     final State state,
773                                     final Nonce nonce,
774                                     final Display display,
775                                     final Prompt prompt,
776                                     final int maxAge,
777                                     final List<LangTag> uiLocales,
778                                     final List<LangTag> claimsLocales,
779                                     final JWT idTokenHint,
780                                     final String loginHint,
781                                     final List<ACR> acrValues,
782                                     final ClaimsRequest claims,
783                                     final JWT requestObject,
784                                     final URI requestURI,
785                                     final CodeChallenge codeChallenge,
786                                     final CodeChallengeMethod codeChallengeMethod) {
787
788                this(uri, rt, rm, scope, clientID, redirectURI, state,
789                        nonce, display, prompt, maxAge, uiLocales, claimsLocales,
790                        idTokenHint, loginHint, acrValues, claims,
791                        requestObject, requestURI, codeChallenge, codeChallengeMethod,
792                        Collections.<String, String>emptyMap());
793        }
794
795
796        /**
797         * Creates a new OpenID Connect authentication request with additional
798         * custom parameters.
799         *
800         * @param uri                 The URI of the OAuth 2.0 authorisation
801         *                            endpoint. May be {@code null} if the
802         *                            {@link #toHTTPRequest} method will not be
803         *                            used.
804         * @param rt                  The response type set. Corresponds to the
805         *                            {@code response_type} parameter. Must
806         *                            specify a valid OpenID Connect response
807         *                            type. Must not be {@code null}.
808         * @param rm                  The response mode. Corresponds to the
809         *                            optional {@code response_mode} parameter.
810         *                            Use of this parameter is not recommended
811         *                            unless a non-default response mode is
812         *                            requested (e.g. form_post).
813         * @param scope               The request scope. Corresponds to the
814         *                            {@code scope} parameter. Must contain an
815         *                            {@link OIDCScopeValue#OPENID openid value}.
816         *                            Must not be {@code null}.
817         * @param clientID            The client identifier. Corresponds to the
818         *                            {@code client_id} parameter. Must not be
819         *                            {@code null}.
820         * @param redirectURI         The redirection URI. Corresponds to the
821         *                            {@code redirect_uri} parameter. Must not
822         *                            be {@code null} unless set by means of
823         *                            the optional {@code request_object} /
824         *                            {@code request_uri} parameter.
825         * @param state               The state. Corresponds to the recommended
826         *                            {@code state} parameter. {@code null} if
827         *                            not specified.
828         * @param nonce               The nonce. Corresponds to the
829         *                            {@code nonce} parameter. May be
830         *                            {@code null} for code flow.
831         * @param display             The requested display type. Corresponds
832         *                            to the optional {@code display}
833         *                            parameter.
834         *                            {@code null} if not specified.
835         * @param prompt              The requested prompt. Corresponds to the
836         *                            optional {@code prompt} parameter.
837         *                            {@code null} if not specified.
838         * @param maxAge              The required maximum authentication age,
839         *                            in seconds. Corresponds to the optional
840         *                            {@code max_age} parameter. -1 if not
841         *                            specified, zero implies
842         *                            {@code prompt=login}.
843         * @param uiLocales           The preferred languages and scripts for
844         *                            the user interface. Corresponds to the
845         *                            optional {@code ui_locales} parameter.
846         *                            {@code null} if not specified.
847         * @param claimsLocales       The preferred languages and scripts for
848         *                            claims being returned. Corresponds to the
849         *                            optional {@code claims_locales}
850         *                            parameter. {@code null} if not specified.
851         * @param idTokenHint         The ID Token hint. Corresponds to the
852         *                            optional {@code id_token_hint} parameter.
853         *                            {@code null} if not specified.
854         * @param loginHint           The login hint. Corresponds to the
855         *                            optional {@code login_hint} parameter.
856         *                            {@code null} if not specified.
857         * @param acrValues           The requested Authentication Context
858         *                            Class Reference values. Corresponds to
859         *                            the optional {@code acr_values}
860         *                            parameter. {@code null} if not specified.
861         * @param claims              The individual claims to be returned.
862         *                            Corresponds to the optional
863         *                            {@code claims} parameter. {@code null} if
864         *                            not specified.
865         * @param requestObject       The request object. Corresponds to the
866         *                            optional {@code request} parameter. Must
867         *                            not be specified together with a request
868         *                            object URI. {@code null} if not
869         *                            specified.
870         * @param requestURI          The request object URI. Corresponds to
871         *                            the optional {@code request_uri}
872         *                            parameter. Must not be specified together
873         *                            with a request object. {@code null} if
874         *                            not specified.
875         * @param codeChallenge       The code challenge for PKCE, {@code null}
876         *                            if not specified.
877         * @param codeChallengeMethod The code challenge method for PKCE,
878         *                            {@code null} if not specified.
879         * @param customParams        Additional custom parameters, empty map
880         *                            or {@code null} if none.
881         */
882        public AuthenticationRequest(final URI uri,
883                                     final ResponseType rt,
884                                     final ResponseMode rm,
885                                     final Scope scope,
886                                     final ClientID clientID,
887                                     final URI redirectURI,
888                                     final State state,
889                                     final Nonce nonce,
890                                     final Display display,
891                                     final Prompt prompt,
892                                     final int maxAge,
893                                     final List<LangTag> uiLocales,
894                                     final List<LangTag> claimsLocales,
895                                     final JWT idTokenHint,
896                                     final String loginHint,
897                                     final List<ACR> acrValues,
898                                     final ClaimsRequest claims,
899                                     final JWT requestObject,
900                                     final URI requestURI,
901                                     final CodeChallenge codeChallenge,
902                                     final CodeChallengeMethod codeChallengeMethod,
903                                     final Map<String,String> customParams) {
904
905                super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams);
906
907                // Redirect URI required unless set in request_object / request_uri
908                if (redirectURI == null && requestObject == null && requestURI == null)
909                        throw new IllegalArgumentException("The redirection URI must not be null");
910                
911                OIDCResponseTypeValidator.validate(rt);
912
913                if (scope == null)
914                        throw new IllegalArgumentException("The scope must not be null");
915
916                if (! scope.contains(OIDCScopeValue.OPENID))
917                        throw new IllegalArgumentException("The scope must include an \"openid\" token");
918                
919                
920                // Nonce required for implicit protocol flow
921                if (rt.impliesImplicitFlow() && nonce == null)
922                        throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow");
923                
924                this.nonce = nonce;
925                
926                // Optional parameters
927                this.display = display;
928                this.prompt = prompt;
929                this.maxAge = maxAge;
930
931                if (uiLocales != null)
932                        this.uiLocales = Collections.unmodifiableList(uiLocales);
933                else
934                        this.uiLocales = null;
935
936                if (claimsLocales != null)
937                        this.claimsLocales = Collections.unmodifiableList(claimsLocales);
938                else
939                        this.claimsLocales = null;
940
941                this.idTokenHint = idTokenHint;
942                this.loginHint = loginHint;
943
944                if (acrValues != null)
945                        this.acrValues = Collections.unmodifiableList(acrValues);
946                else
947                        this.acrValues = null;
948
949                this.claims = claims;
950
951                if (requestObject != null && requestURI != null)
952                        throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both");
953
954                this.requestObject = requestObject;
955                this.requestURI = requestURI;
956        }
957
958
959        /**
960         * Returns the registered (standard) OpenID Connect authentication
961         * request parameter names.
962         *
963         * @return The registered OpenID Connect authentication request
964         *         parameter names, as a unmodifiable set.
965         */
966        public static Set<String> getRegisteredParameterNames() {
967
968                return REGISTERED_PARAMETER_NAMES;
969        }
970        
971        
972        /**
973         * Gets the nonce. Corresponds to the conditionally optional 
974         * {@code nonce} parameter.
975         *
976         * @return The nonce, {@code null} if not specified.
977         */
978        public Nonce getNonce() {
979        
980                return nonce;
981        }
982        
983        
984        /**
985         * Gets the requested display type. Corresponds to the optional
986         * {@code display} parameter.
987         *
988         * @return The requested display type, {@code null} if not specified.
989         */
990        public Display getDisplay() {
991        
992                return display;
993        }
994        
995        
996        /**
997         * Gets the requested prompt. Corresponds to the optional 
998         * {@code prompt} parameter.
999         *
1000         * @return The requested prompt, {@code null} if not specified.
1001         */
1002        public Prompt getPrompt() {
1003        
1004                return prompt;
1005        }
1006
1007
1008        /**
1009         * Gets the required maximum authentication age. Corresponds to the
1010         * optional {@code max_age} parameter.
1011         *
1012         * @return The maximum authentication age, in seconds; -1 if not
1013         *         specified, zero implies {@code prompt=login}.
1014         */
1015        public int getMaxAge() {
1016        
1017                return maxAge;
1018        }
1019
1020
1021        /**
1022         * Gets the end-user's preferred languages and scripts for the user
1023         * interface, ordered by preference. Corresponds to the optional
1024         * {@code ui_locales} parameter.
1025         *
1026         * @return The preferred UI locales, {@code null} if not specified.
1027         */
1028        public List<LangTag> getUILocales() {
1029
1030                return uiLocales;
1031        }
1032
1033
1034        /**
1035         * Gets the end-user's preferred languages and scripts for the claims
1036         * being returned, ordered by preference. Corresponds to the optional
1037         * {@code claims_locales} parameter.
1038         *
1039         * @return The preferred claims locales, {@code null} if not specified.
1040         */
1041        public List<LangTag> getClaimsLocales() {
1042
1043                return claimsLocales;
1044        }
1045
1046
1047        /**
1048         * Gets the ID Token hint. Corresponds to the conditionally optional 
1049         * {@code id_token_hint} parameter.
1050         *
1051         * @return The ID Token hint, {@code null} if not specified.
1052         */
1053        public JWT getIDTokenHint() {
1054        
1055                return idTokenHint;
1056        }
1057
1058
1059        /**
1060         * Gets the login hint. Corresponds to the optional {@code login_hint} 
1061         * parameter.
1062         *
1063         * @return The login hint, {@code null} if not specified.
1064         */
1065        public String getLoginHint() {
1066
1067                return loginHint;
1068        }
1069
1070
1071        /**
1072         * Gets the requested Authentication Context Class Reference values.
1073         * Corresponds to the optional {@code acr_values} parameter.
1074         *
1075         * @return The requested ACR values, {@code null} if not specified.
1076         */
1077        public List<ACR> getACRValues() {
1078
1079                return acrValues;
1080        }
1081
1082
1083        /**
1084         * Gets the individual claims to be returned. Corresponds to the 
1085         * optional {@code claims} parameter.
1086         *
1087         * @return The individual claims to be returned, {@code null} if not
1088         *         specified.
1089         */
1090        public ClaimsRequest getClaims() {
1091
1092                return claims;
1093        }
1094        
1095        
1096        /**
1097         * Gets the request object. Corresponds to the optional {@code request} 
1098         * parameter.
1099         *
1100         * @return The request object, {@code null} if not specified.
1101         */
1102        public JWT getRequestObject() {
1103        
1104                return requestObject;
1105        }
1106        
1107        
1108        /**
1109         * Gets the request object URI. Corresponds to the optional
1110         * {@code request_uri} parameter.
1111         *
1112         * @return The request object URI, {@code null} if not specified.
1113         */
1114        public URI getRequestURI() {
1115        
1116                return requestURI;
1117        }
1118        
1119        
1120        /**
1121         * Returns {@code true} if this authentication request specifies an
1122         * OpenID Connect request object (directly through the {@code request} 
1123         * parameter or by reference through the {@code request_uri} parameter).
1124         *
1125         * @return {@code true} if a request object is specified, else 
1126         *         {@code false}.
1127         */
1128        public boolean specifiesRequestObject() {
1129        
1130                return requestObject != null || requestURI != null;
1131        }
1132
1133
1134        @Override
1135        public Map<String,String> toParameters() {
1136
1137                Map <String,String> params = super.toParameters();
1138                
1139                if (nonce != null)
1140                        params.put("nonce", nonce.toString());
1141                
1142                if (display != null)
1143                        params.put("display", display.toString());
1144                
1145                if (prompt != null)
1146                        params.put("prompt", prompt.toString());
1147
1148                if (maxAge >= 0)
1149                        params.put("max_age", "" + maxAge);
1150
1151                if (uiLocales != null) {
1152
1153                        StringBuilder sb = new StringBuilder();
1154
1155                        for (LangTag locale: uiLocales) {
1156
1157                                if (sb.length() > 0)
1158                                        sb.append(' ');
1159
1160                                sb.append(locale.toString());
1161                        }
1162
1163                        params.put("ui_locales", sb.toString());
1164                }
1165
1166                if (claimsLocales != null) {
1167
1168                        StringBuilder sb = new StringBuilder();
1169
1170                        for (LangTag locale: claimsLocales) {
1171
1172                                if (sb.length() > 0)
1173                                        sb.append(' ');
1174
1175                                sb.append(locale.toString());
1176                        }
1177
1178                        params.put("claims_locales", sb.toString());
1179                }
1180
1181                if (idTokenHint != null) {
1182                
1183                        try {
1184                                params.put("id_token_hint", idTokenHint.serialize());
1185                                
1186                        } catch (IllegalStateException e) {
1187                        
1188                                throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e);
1189                        }
1190                }
1191
1192                if (loginHint != null)
1193                        params.put("login_hint", loginHint);
1194
1195                if (acrValues != null) {
1196
1197                        StringBuilder sb = new StringBuilder();
1198
1199                        for (ACR acr: acrValues) {
1200
1201                                if (sb.length() > 0)
1202                                        sb.append(' ');
1203
1204                                sb.append(acr.toString());
1205                        }
1206
1207                        params.put("acr_values", sb.toString());
1208                }
1209                        
1210
1211                if (claims != null)
1212                        params.put("claims", claims.toJSONObject().toString());
1213                
1214                if (requestObject != null) {
1215                
1216                        try {
1217                                params.put("request", requestObject.serialize());
1218                                
1219                        } catch (IllegalStateException e) {
1220                        
1221                                throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e);
1222                        }
1223                }
1224                
1225                if (requestURI != null)
1226                        params.put("request_uri", requestURI.toString());
1227
1228                return params;
1229        }
1230
1231
1232        /**
1233         * Parses an OpenID Connect authentication request from the specified
1234         * parameters.
1235         *
1236         * <p>Example parameters:
1237         *
1238         * <pre>
1239         * response_type = token id_token
1240         * client_id     = s6BhdRkqt3
1241         * redirect_uri  = https://client.example.com/cb
1242         * scope         = openid profile
1243         * state         = af0ifjsldkj
1244         * nonce         = -0S6_WzA2Mj
1245         * </pre>
1246         *
1247         * @param params The parameters. Must not be {@code null}.
1248         *
1249         * @return The OpenID Connect authentication request.
1250         *
1251         * @throws ParseException If the parameters couldn't be parsed to an
1252         *                        OpenID Connect authentication request.
1253         */
1254        public static AuthenticationRequest parse(final Map<String,String> params)
1255                throws ParseException {
1256
1257                return parse(null, params);
1258        }
1259
1260
1261        /**
1262         * Parses an OpenID Connect authentication request from the specified
1263         * parameters.
1264         *
1265         * <p>Example parameters:
1266         *
1267         * <pre>
1268         * response_type = token id_token
1269         * client_id     = s6BhdRkqt3
1270         * redirect_uri  = https://client.example.com/cb
1271         * scope         = openid profile
1272         * state         = af0ifjsldkj
1273         * nonce         = -0S6_WzA2Mj
1274         * </pre>
1275         *
1276         * @param uri    The URI of the OAuth 2.0 authorisation endpoint. May
1277         *               be {@code null} if the {@link #toHTTPRequest} method
1278         *               will not be used.
1279         * @param params The parameters. Must not be {@code null}.
1280         *
1281         * @return The OpenID Connect authentication request.
1282         *
1283         * @throws ParseException If the parameters couldn't be parsed to an
1284         *                        OpenID Connect authentication request.
1285         */
1286        public static AuthenticationRequest parse(final URI uri, final Map<String,String> params)
1287                throws ParseException {
1288
1289                // Parse and validate the core OAuth 2.0 autz request params in 
1290                // the context of OIDC
1291                AuthorizationRequest ar = AuthorizationRequest.parse(uri, params);
1292
1293                ClientID clientID = ar.getClientID();
1294                State state = ar.getState();
1295                ResponseMode rm = ar.getResponseMode();
1296
1297                // Required in OIDC, check later after optional request_object / request_uri is parsed
1298                URI redirectURI = ar.getRedirectionURI();
1299
1300                ResponseType rt = ar.getResponseType();
1301                
1302                try {
1303                        OIDCResponseTypeValidator.validate(rt);
1304                        
1305                } catch (IllegalArgumentException e) {
1306                        String msg = "Unsupported \"response_type\" parameter: " + e.getMessage();
1307                        throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg),
1308                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1309                }
1310                
1311                // Required in OIDC, must include "openid" parameter
1312                Scope scope = ar.getScope();
1313
1314                if (scope == null) {
1315                        String msg = "Missing \"scope\" parameter";
1316                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1317                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1318                }
1319
1320                if (! scope.contains(OIDCScopeValue.OPENID)) {
1321                        String msg = "The scope must include an \"openid\" value";
1322                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1323                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1324                }
1325
1326
1327                // Parse the remaining OIDC parameters
1328                Nonce nonce = Nonce.parse(params.get("nonce"));
1329                
1330                // Nonce required in implicit flow
1331                if (rt.impliesImplicitFlow() && nonce == null) {
1332                        String msg = "Missing \"nonce\" parameter: Required in implicit flow";
1333                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1334                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1335                }
1336                
1337                Display display = null;
1338
1339                if (params.containsKey("display")) {
1340                        try {
1341                                display = Display.parse(params.get("display"));
1342
1343                        } catch (ParseException e) {
1344                                String msg = "Invalid \"display\" parameter: " + e.getMessage();
1345                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1346                                        clientID, redirectURI, ar.impliedResponseMode(), state, e);
1347                        }
1348                }
1349                
1350                
1351                Prompt prompt;
1352                
1353                try {
1354                        prompt = Prompt.parse(params.get("prompt"));
1355                                
1356                } catch (ParseException e) {
1357                        String msg = "Invalid \"prompt\" parameter: " + e.getMessage();
1358                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1359                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1360                }
1361
1362
1363                String v = params.get("max_age");
1364
1365                int maxAge = -1;
1366
1367                if (StringUtils.isNotBlank(v)) {
1368
1369                        try {
1370                                maxAge = Integer.parseInt(v);
1371
1372                        } catch (NumberFormatException e) {
1373                                String msg = "Invalid \"max_age\" parameter: " + v;
1374                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1375                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1376                        }
1377                }
1378
1379
1380                v = params.get("ui_locales");
1381
1382                List<LangTag> uiLocales = null;
1383
1384                if (StringUtils.isNotBlank(v)) {
1385
1386                        uiLocales = new LinkedList<>();
1387
1388                        StringTokenizer st = new StringTokenizer(v, " ");
1389
1390                        while (st.hasMoreTokens()) {
1391
1392                                try {
1393                                        uiLocales.add(LangTag.parse(st.nextToken()));
1394
1395                                } catch (LangTagException e) {
1396                                        String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage();
1397                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1398                                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1399                                }
1400                        }
1401                }
1402
1403
1404                v = params.get("claims_locales");
1405
1406                List<LangTag> claimsLocales = null;
1407
1408                if (StringUtils.isNotBlank(v)) {
1409
1410                        claimsLocales = new LinkedList<>();
1411
1412                        StringTokenizer st = new StringTokenizer(v, " ");
1413
1414                        while (st.hasMoreTokens()) {
1415
1416                                try {
1417                                        claimsLocales.add(LangTag.parse(st.nextToken()));
1418
1419                                } catch (LangTagException e) {
1420                                        String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage();
1421                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1422                                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1423                                }
1424                        }
1425                }
1426
1427
1428                v = params.get("id_token_hint");
1429                
1430                JWT idTokenHint = null;
1431                
1432                if (StringUtils.isNotBlank(v)) {
1433                
1434                        try {
1435                                idTokenHint = JWTParser.parse(v);
1436                                
1437                        } catch (java.text.ParseException e) {
1438                                String msg = "Invalid \"id_token_hint\" parameter: " + e.getMessage();
1439                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1440                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1441                        }
1442                }
1443
1444                String loginHint = params.get("login_hint");
1445
1446
1447                v = params.get("acr_values");
1448
1449                List<ACR> acrValues = null;
1450
1451                if (StringUtils.isNotBlank(v)) {
1452
1453                        acrValues = new LinkedList<>();
1454
1455                        StringTokenizer st = new StringTokenizer(v, " ");
1456
1457                        while (st.hasMoreTokens()) {
1458
1459                                acrValues.add(new ACR(st.nextToken()));
1460                        }
1461                }
1462
1463
1464                v = params.get("claims");
1465
1466                ClaimsRequest claims = null;
1467
1468                if (StringUtils.isNotBlank(v)) {
1469
1470                        JSONObject jsonObject;
1471
1472                        try {
1473                                jsonObject = JSONObjectUtils.parse(v);
1474
1475                        } catch (ParseException e) {
1476                                String msg = "Invalid \"claims\" parameter: " + e.getMessage();
1477                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1478                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1479                        }
1480
1481                        // Parse exceptions silently ignored
1482                        claims = ClaimsRequest.parse(jsonObject);
1483                }
1484                
1485                
1486                v = params.get("request_uri");
1487                
1488                URI requestURI = null;
1489                
1490                if (StringUtils.isNotBlank(v)) {
1491
1492                        try {
1493                                requestURI = new URI(v);
1494                
1495                        } catch (URISyntaxException e) {
1496                                String msg = "Invalid \"request_uri\" parameter: " + e.getMessage();
1497                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1498                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1499                        }
1500                }
1501
1502                v = params.get("request");
1503
1504                JWT requestObject = null;
1505
1506                if (StringUtils.isNotBlank(v)) {
1507
1508                        // request_object and request_uri must not be defined at the same time
1509                        if (requestURI != null) {
1510                                String msg = "Invalid request: Found mutually exclusive \"request\" and \"request_uri\" parameters";
1511                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1512                                                         clientID, redirectURI, ar.impliedResponseMode(), state, null);
1513                        }
1514
1515                        try {
1516                                requestObject = JWTParser.parse(v);
1517                                
1518                        } catch (java.text.ParseException e) {
1519                                String msg = "Invalid \"request_object\" parameter: " + e.getMessage();
1520                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1521                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1522                        }
1523                }
1524
1525
1526                // Redirect URI required unless request_object / request_uri present
1527                if (redirectURI == null && requestObject == null && requestURI == null) {
1528                        String msg = "Missing \"redirect_uri\" parameter";
1529                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1530                                clientID, null, ar.impliedResponseMode(), state);
1531                }
1532
1533                // Parse additional custom parameters
1534                Map<String,String> customParams = null;
1535
1536                for (Map.Entry<String,String> p: params.entrySet()) {
1537
1538                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1539                                // We have a custom parameter
1540                                if (customParams == null) {
1541                                        customParams = new HashMap<>();
1542                                }
1543                                customParams.put(p.getKey(), p.getValue());
1544                        }
1545                }
1546
1547
1548                return new AuthenticationRequest(
1549                        uri, rt, rm, scope, clientID, redirectURI, state, nonce,
1550                        display, prompt, maxAge, uiLocales, claimsLocales,
1551                        idTokenHint, loginHint, acrValues, claims, requestObject, requestURI,
1552                        ar.getCodeChallenge(), ar.getCodeChallengeMethod(),
1553                        customParams);
1554        }
1555        
1556        
1557        /**
1558         * Parses an OpenID Connect authentication request from the specified
1559         * URI query string.
1560         *
1561         * <p>Example URI query string:
1562         *
1563         * <pre>
1564         * response_type=token%20id_token
1565         * &amp;client_id=s6BhdRkqt3
1566         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1567         * &amp;scope=openid%20profile
1568         * &amp;state=af0ifjsldkj
1569         * &amp;nonce=n-0S6_WzA2Mj
1570         * </pre>
1571         *
1572         * @param query The URI query string. Must not be {@code null}.
1573         *
1574         * @return The OpenID Connect authentication request.
1575         *
1576         * @throws ParseException If the query string couldn't be parsed to an 
1577         *                        OpenID Connect authentication request.
1578         */
1579        public static AuthenticationRequest parse(final String query)
1580                throws ParseException {
1581        
1582                return parse(null, URLUtils.parseParameters(query));
1583        }
1584
1585
1586        /**
1587         * Parses an OpenID Connect authentication request from the specified
1588         * URI query string.
1589         *
1590         * <p>Example URI query string:
1591         *
1592         * <pre>
1593         * response_type=token%20id_token
1594         * &amp;client_id=s6BhdRkqt3
1595         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1596         * &amp;scope=openid%20profile
1597         * &amp;state=af0ifjsldkj
1598         * &amp;nonce=n-0S6_WzA2Mj
1599         * </pre>
1600         *
1601         * @param uri   The URI of the OAuth 2.0 authorisation endpoint. May be
1602         *              {@code null} if the {@link #toHTTPRequest} method will
1603         *              not be used.
1604         * @param query The URI query string. Must not be {@code null}.
1605         *
1606         * @return The OpenID Connect authentication request.
1607         *
1608         * @throws ParseException If the query string couldn't be parsed to an
1609         *                        OpenID Connect authentication request.
1610         */
1611        public static AuthenticationRequest parse(final URI uri, final String query)
1612                throws ParseException {
1613
1614                return parse(uri, URLUtils.parseParameters(query));
1615        }
1616
1617
1618        /**
1619         * Parses an OpenID Connect authentication request from the specified
1620         * URI.
1621         *
1622         * <p>Example URI:
1623         *
1624         * <pre>
1625         * https://server.example.com/authorize?
1626         * response_type=token%20id_token
1627         * &amp;client_id=s6BhdRkqt3
1628         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1629         * &amp;scope=openid%20profile
1630         * &amp;state=af0ifjsldkj
1631         * &amp;nonce=n-0S6_WzA2Mj
1632         * </pre>
1633         *
1634         * @param uri The URI. Must not be {@code null}.
1635         *
1636         * @return The OpenID Connect authentication request.
1637         *
1638         * @throws ParseException If the query string couldn't be parsed to an
1639         *                        OpenID Connect authentication request.
1640         */
1641        public static AuthenticationRequest parse(final URI uri)
1642                throws ParseException {
1643
1644                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1645        }
1646        
1647        
1648        /**
1649         * Parses an authentication request from the specified HTTP GET or HTTP
1650         * POST request.
1651         *
1652         * <p>Example HTTP request (GET):
1653         *
1654         * <pre>
1655         * https://server.example.com/op/authorize?
1656         * response_type=code%20id_token
1657         * &amp;client_id=s6BhdRkqt3
1658         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1659         * &amp;scope=openid
1660         * &amp;nonce=n-0S6_WzA2Mj
1661         * &amp;state=af0ifjsldkj
1662         * </pre>
1663         *
1664         * @param httpRequest The HTTP request. Must not be {@code null}.
1665         *
1666         * @return The OpenID Connect authentication request.
1667         *
1668         * @throws ParseException If the HTTP request couldn't be parsed to an 
1669         *                        OpenID Connect authentication request.
1670         */
1671        public static AuthenticationRequest parse(final HTTPRequest httpRequest)
1672                throws ParseException {
1673                
1674                String query = httpRequest.getQuery();
1675                
1676                if (query == null)
1677                        throw new ParseException("Missing URI query string");
1678
1679                URI endpointURI;
1680
1681                try {
1682                        endpointURI = httpRequest.getURL().toURI();
1683
1684                } catch (URISyntaxException e) {
1685
1686                        throw new ParseException(e.getMessage(), e);
1687                }
1688                
1689                return parse(endpointURI, query);
1690        }
1691}