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, 0 if not 
106         * specified (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, 0 if
230                 * not specified (optional).
231                 */
232                private int maxAge;
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, 0, 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. Zero if not
841         *                            specified.
842         * @param uiLocales           The preferred languages and scripts for
843         *                            the user interface. Corresponds to the
844         *                            optional {@code ui_locales} parameter.
845         *                            {@code null} if not specified.
846         * @param claimsLocales       The preferred languages and scripts for
847         *                            claims being returned. Corresponds to the
848         *                            optional {@code claims_locales}
849         *                            parameter. {@code null} if not specified.
850         * @param idTokenHint         The ID Token hint. Corresponds to the
851         *                            optional {@code id_token_hint} parameter.
852         *                            {@code null} if not specified.
853         * @param loginHint           The login hint. Corresponds to the
854         *                            optional {@code login_hint} parameter.
855         *                            {@code null} if not specified.
856         * @param acrValues           The requested Authentication Context
857         *                            Class Reference values. Corresponds to
858         *                            the optional {@code acr_values}
859         *                            parameter. {@code null} if not specified.
860         * @param claims              The individual claims to be returned.
861         *                            Corresponds to the optional
862         *                            {@code claims} parameter. {@code null} if
863         *                            not specified.
864         * @param requestObject       The request object. Corresponds to the
865         *                            optional {@code request} parameter. Must
866         *                            not be specified together with a request
867         *                            object URI. {@code null} if not
868         *                            specified.
869         * @param requestURI          The request object URI. Corresponds to
870         *                            the optional {@code request_uri}
871         *                            parameter. Must not be specified together
872         *                            with a request object. {@code null} if
873         *                            not specified.
874         * @param codeChallenge       The code challenge for PKCE, {@code null}
875         *                            if not specified.
876         * @param codeChallengeMethod The code challenge method for PKCE,
877         *                            {@code null} if not specified.
878         * @param customParams        Additional custom parameters, empty map
879         *                            or {@code null} if none.
880         */
881        public AuthenticationRequest(final URI uri,
882                                     final ResponseType rt,
883                                     final ResponseMode rm,
884                                     final Scope scope,
885                                     final ClientID clientID,
886                                     final URI redirectURI,
887                                     final State state,
888                                     final Nonce nonce,
889                                     final Display display,
890                                     final Prompt prompt,
891                                     final int maxAge,
892                                     final List<LangTag> uiLocales,
893                                     final List<LangTag> claimsLocales,
894                                     final JWT idTokenHint,
895                                     final String loginHint,
896                                     final List<ACR> acrValues,
897                                     final ClaimsRequest claims,
898                                     final JWT requestObject,
899                                     final URI requestURI,
900                                     final CodeChallenge codeChallenge,
901                                     final CodeChallengeMethod codeChallengeMethod,
902                                     final Map<String,String> customParams) {
903
904                super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams);
905
906                // Redirect URI required unless set in request_object / request_uri
907                if (redirectURI == null && requestObject == null && requestURI == null)
908                        throw new IllegalArgumentException("The redirection URI must not be null");
909                
910                OIDCResponseTypeValidator.validate(rt);
911
912                if (scope == null)
913                        throw new IllegalArgumentException("The scope must not be null");
914
915                if (! scope.contains(OIDCScopeValue.OPENID))
916                        throw new IllegalArgumentException("The scope must include an \"openid\" token");
917                
918                
919                // Nonce required for implicit protocol flow
920                if (rt.impliesImplicitFlow() && nonce == null)
921                        throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow");
922                
923                this.nonce = nonce;
924                
925                // Optional parameters
926                this.display = display;
927                this.prompt = prompt;
928                this.maxAge = maxAge;
929
930                if (uiLocales != null)
931                        this.uiLocales = Collections.unmodifiableList(uiLocales);
932                else
933                        this.uiLocales = null;
934
935                if (claimsLocales != null)
936                        this.claimsLocales = Collections.unmodifiableList(claimsLocales);
937                else
938                        this.claimsLocales = null;
939
940                this.idTokenHint = idTokenHint;
941                this.loginHint = loginHint;
942
943                if (acrValues != null)
944                        this.acrValues = Collections.unmodifiableList(acrValues);
945                else
946                        this.acrValues = null;
947
948                this.claims = claims;
949
950                if (requestObject != null && requestURI != null)
951                        throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both");
952
953                this.requestObject = requestObject;
954                this.requestURI = requestURI;
955        }
956
957
958        /**
959         * Returns the registered (standard) OpenID Connect authentication
960         * request parameter names.
961         *
962         * @return The registered OpenID Connect authentication request
963         *         parameter names, as a unmodifiable set.
964         */
965        public static Set<String> getRegisteredParameterNames() {
966
967                return REGISTERED_PARAMETER_NAMES;
968        }
969        
970        
971        /**
972         * Gets the nonce. Corresponds to the conditionally optional 
973         * {@code nonce} parameter.
974         *
975         * @return The nonce, {@code null} if not specified.
976         */
977        public Nonce getNonce() {
978        
979                return nonce;
980        }
981        
982        
983        /**
984         * Gets the requested display type. Corresponds to the optional
985         * {@code display} parameter.
986         *
987         * @return The requested display type, {@code null} if not specified.
988         */
989        public Display getDisplay() {
990        
991                return display;
992        }
993        
994        
995        /**
996         * Gets the requested prompt. Corresponds to the optional 
997         * {@code prompt} parameter.
998         *
999         * @return The requested prompt, {@code null} if not specified.
1000         */
1001        public Prompt getPrompt() {
1002        
1003                return prompt;
1004        }
1005
1006
1007        /**
1008         * Gets the required maximum authentication age. Corresponds to the
1009         * optional {@code max_age} parameter.
1010         *
1011         * @return The maximum authentication age, in seconds; 0 if not 
1012         *         specified.
1013         */
1014        public int getMaxAge() {
1015        
1016                return maxAge;
1017        }
1018
1019
1020        /**
1021         * Gets the end-user's preferred languages and scripts for the user
1022         * interface, ordered by preference. Corresponds to the optional
1023         * {@code ui_locales} parameter.
1024         *
1025         * @return The preferred UI locales, {@code null} if not specified.
1026         */
1027        public List<LangTag> getUILocales() {
1028
1029                return uiLocales;
1030        }
1031
1032
1033        /**
1034         * Gets the end-user's preferred languages and scripts for the claims
1035         * being returned, ordered by preference. Corresponds to the optional
1036         * {@code claims_locales} parameter.
1037         *
1038         * @return The preferred claims locales, {@code null} if not specified.
1039         */
1040        public List<LangTag> getClaimsLocales() {
1041
1042                return claimsLocales;
1043        }
1044
1045
1046        /**
1047         * Gets the ID Token hint. Corresponds to the conditionally optional 
1048         * {@code id_token_hint} parameter.
1049         *
1050         * @return The ID Token hint, {@code null} if not specified.
1051         */
1052        public JWT getIDTokenHint() {
1053        
1054                return idTokenHint;
1055        }
1056
1057
1058        /**
1059         * Gets the login hint. Corresponds to the optional {@code login_hint} 
1060         * parameter.
1061         *
1062         * @return The login hint, {@code null} if not specified.
1063         */
1064        public String getLoginHint() {
1065
1066                return loginHint;
1067        }
1068
1069
1070        /**
1071         * Gets the requested Authentication Context Class Reference values.
1072         * Corresponds to the optional {@code acr_values} parameter.
1073         *
1074         * @return The requested ACR values, {@code null} if not specified.
1075         */
1076        public List<ACR> getACRValues() {
1077
1078                return acrValues;
1079        }
1080
1081
1082        /**
1083         * Gets the individual claims to be returned. Corresponds to the 
1084         * optional {@code claims} parameter.
1085         *
1086         * @return The individual claims to be returned, {@code null} if not
1087         *         specified.
1088         */
1089        public ClaimsRequest getClaims() {
1090
1091                return claims;
1092        }
1093        
1094        
1095        /**
1096         * Gets the request object. Corresponds to the optional {@code request} 
1097         * parameter.
1098         *
1099         * @return The request object, {@code null} if not specified.
1100         */
1101        public JWT getRequestObject() {
1102        
1103                return requestObject;
1104        }
1105        
1106        
1107        /**
1108         * Gets the request object URI. Corresponds to the optional
1109         * {@code request_uri} parameter.
1110         *
1111         * @return The request object URI, {@code null} if not specified.
1112         */
1113        public URI getRequestURI() {
1114        
1115                return requestURI;
1116        }
1117        
1118        
1119        /**
1120         * Returns {@code true} if this authentication request specifies an
1121         * OpenID Connect request object (directly through the {@code request} 
1122         * parameter or by reference through the {@code request_uri} parameter).
1123         *
1124         * @return {@code true} if a request object is specified, else 
1125         *         {@code false}.
1126         */
1127        public boolean specifiesRequestObject() {
1128        
1129                return requestObject != null || requestURI != null;
1130        }
1131
1132
1133        @Override
1134        public Map<String,String> toParameters() {
1135
1136                Map <String,String> params = super.toParameters();
1137                
1138                if (nonce != null)
1139                        params.put("nonce", nonce.toString());
1140                
1141                if (display != null)
1142                        params.put("display", display.toString());
1143                
1144                if (prompt != null)
1145                        params.put("prompt", prompt.toString());
1146
1147                if (maxAge > 0)
1148                        params.put("max_age", "" + maxAge);
1149
1150                if (uiLocales != null) {
1151
1152                        StringBuilder sb = new StringBuilder();
1153
1154                        for (LangTag locale: uiLocales) {
1155
1156                                if (sb.length() > 0)
1157                                        sb.append(' ');
1158
1159                                sb.append(locale.toString());
1160                        }
1161
1162                        params.put("ui_locales", sb.toString());
1163                }
1164
1165                if (claimsLocales != null) {
1166
1167                        StringBuilder sb = new StringBuilder();
1168
1169                        for (LangTag locale: claimsLocales) {
1170
1171                                if (sb.length() > 0)
1172                                        sb.append(' ');
1173
1174                                sb.append(locale.toString());
1175                        }
1176
1177                        params.put("claims_locales", sb.toString());
1178                }
1179
1180                if (idTokenHint != null) {
1181                
1182                        try {
1183                                params.put("id_token_hint", idTokenHint.serialize());
1184                                
1185                        } catch (IllegalStateException e) {
1186                        
1187                                throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e);
1188                        }
1189                }
1190
1191                if (loginHint != null)
1192                        params.put("login_hint", loginHint);
1193
1194                if (acrValues != null) {
1195
1196                        StringBuilder sb = new StringBuilder();
1197
1198                        for (ACR acr: acrValues) {
1199
1200                                if (sb.length() > 0)
1201                                        sb.append(' ');
1202
1203                                sb.append(acr.toString());
1204                        }
1205
1206                        params.put("acr_values", sb.toString());
1207                }
1208                        
1209
1210                if (claims != null)
1211                        params.put("claims", claims.toJSONObject().toString());
1212                
1213                if (requestObject != null) {
1214                
1215                        try {
1216                                params.put("request", requestObject.serialize());
1217                                
1218                        } catch (IllegalStateException e) {
1219                        
1220                                throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e);
1221                        }
1222                }
1223                
1224                if (requestURI != null)
1225                        params.put("request_uri", requestURI.toString());
1226
1227                return params;
1228        }
1229
1230
1231        /**
1232         * Parses an OpenID Connect authentication request from the specified
1233         * parameters.
1234         *
1235         * <p>Example parameters:
1236         *
1237         * <pre>
1238         * response_type = token id_token
1239         * client_id     = s6BhdRkqt3
1240         * redirect_uri  = https://client.example.com/cb
1241         * scope         = openid profile
1242         * state         = af0ifjsldkj
1243         * nonce         = -0S6_WzA2Mj
1244         * </pre>
1245         *
1246         * @param params The parameters. Must not be {@code null}.
1247         *
1248         * @return The OpenID Connect authentication request.
1249         *
1250         * @throws ParseException If the parameters couldn't be parsed to an
1251         *                        OpenID Connect authentication request.
1252         */
1253        public static AuthenticationRequest parse(final Map<String,String> params)
1254                throws ParseException {
1255
1256                return parse(null, params);
1257        }
1258
1259
1260        /**
1261         * Parses an OpenID Connect authentication request from the specified
1262         * parameters.
1263         *
1264         * <p>Example parameters:
1265         *
1266         * <pre>
1267         * response_type = token id_token
1268         * client_id     = s6BhdRkqt3
1269         * redirect_uri  = https://client.example.com/cb
1270         * scope         = openid profile
1271         * state         = af0ifjsldkj
1272         * nonce         = -0S6_WzA2Mj
1273         * </pre>
1274         *
1275         * @param uri    The URI of the OAuth 2.0 authorisation endpoint. May
1276         *               be {@code null} if the {@link #toHTTPRequest} method
1277         *               will not be used.
1278         * @param params The parameters. Must not be {@code null}.
1279         *
1280         * @return The OpenID Connect authentication request.
1281         *
1282         * @throws ParseException If the parameters couldn't be parsed to an
1283         *                        OpenID Connect authentication request.
1284         */
1285        public static AuthenticationRequest parse(final URI uri, final Map<String,String> params)
1286                throws ParseException {
1287
1288                // Parse and validate the core OAuth 2.0 autz request params in 
1289                // the context of OIDC
1290                AuthorizationRequest ar = AuthorizationRequest.parse(uri, params);
1291
1292                ClientID clientID = ar.getClientID();
1293                State state = ar.getState();
1294                ResponseMode rm = ar.getResponseMode();
1295
1296                // Required in OIDC, check later after optional request_object / request_uri is parsed
1297                URI redirectURI = ar.getRedirectionURI();
1298
1299                ResponseType rt = ar.getResponseType();
1300                
1301                try {
1302                        OIDCResponseTypeValidator.validate(rt);
1303                        
1304                } catch (IllegalArgumentException e) {
1305                        String msg = "Unsupported \"response_type\" parameter: " + e.getMessage();
1306                        throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg),
1307                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1308                }
1309                
1310                // Required in OIDC, must include "openid" parameter
1311                Scope scope = ar.getScope();
1312
1313                if (scope == null) {
1314                        String msg = "Missing \"scope\" parameter";
1315                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1316                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1317                }
1318
1319                if (! scope.contains(OIDCScopeValue.OPENID)) {
1320                        String msg = "The scope must include an \"openid\" value";
1321                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1322                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1323                }
1324
1325
1326                // Parse the remaining OIDC parameters
1327                Nonce nonce = Nonce.parse(params.get("nonce"));
1328                
1329                // Nonce required in implicit flow
1330                if (rt.impliesImplicitFlow() && nonce == null) {
1331                        String msg = "Missing \"nonce\" parameter: Required in implicit flow";
1332                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1333                                                 clientID, redirectURI, ar.impliedResponseMode(), state);
1334                }
1335                
1336                Display display = null;
1337
1338                if (params.containsKey("display")) {
1339                        try {
1340                                display = Display.parse(params.get("display"));
1341
1342                        } catch (ParseException e) {
1343                                String msg = "Invalid \"display\" parameter: " + e.getMessage();
1344                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1345                                        clientID, redirectURI, ar.impliedResponseMode(), state, e);
1346                        }
1347                }
1348                
1349                
1350                Prompt prompt;
1351                
1352                try {
1353                        prompt = Prompt.parse(params.get("prompt"));
1354                                
1355                } catch (ParseException e) {
1356                        String msg = "Invalid \"prompt\" parameter: " + e.getMessage();
1357                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1358                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1359                }
1360
1361
1362                String v = params.get("max_age");
1363
1364                int maxAge = 0;
1365
1366                if (StringUtils.isNotBlank(v)) {
1367
1368                        try {
1369                                maxAge = Integer.parseInt(v);
1370
1371                        } catch (NumberFormatException e) {
1372                                String msg = "Invalid \"max_age\" parameter: " + v;
1373                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1374                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1375                        }
1376                }
1377
1378
1379                v = params.get("ui_locales");
1380
1381                List<LangTag> uiLocales = null;
1382
1383                if (StringUtils.isNotBlank(v)) {
1384
1385                        uiLocales = new LinkedList<>();
1386
1387                        StringTokenizer st = new StringTokenizer(v, " ");
1388
1389                        while (st.hasMoreTokens()) {
1390
1391                                try {
1392                                        uiLocales.add(LangTag.parse(st.nextToken()));
1393
1394                                } catch (LangTagException e) {
1395                                        String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage();
1396                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1397                                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1398                                }
1399                        }
1400                }
1401
1402
1403                v = params.get("claims_locales");
1404
1405                List<LangTag> claimsLocales = null;
1406
1407                if (StringUtils.isNotBlank(v)) {
1408
1409                        claimsLocales = new LinkedList<>();
1410
1411                        StringTokenizer st = new StringTokenizer(v, " ");
1412
1413                        while (st.hasMoreTokens()) {
1414
1415                                try {
1416                                        claimsLocales.add(LangTag.parse(st.nextToken()));
1417
1418                                } catch (LangTagException e) {
1419                                        String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage();
1420                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1421                                                                 clientID, redirectURI, ar.impliedResponseMode(), state, e);
1422                                }
1423                        }
1424                }
1425
1426
1427                v = params.get("id_token_hint");
1428                
1429                JWT idTokenHint = null;
1430                
1431                if (StringUtils.isNotBlank(v)) {
1432                
1433                        try {
1434                                idTokenHint = JWTParser.parse(v);
1435                                
1436                        } catch (java.text.ParseException e) {
1437                                String msg = "Invalid \"id_token_hint\" parameter: " + e.getMessage();
1438                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1439                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1440                        }
1441                }
1442
1443                String loginHint = params.get("login_hint");
1444
1445
1446                v = params.get("acr_values");
1447
1448                List<ACR> acrValues = null;
1449
1450                if (StringUtils.isNotBlank(v)) {
1451
1452                        acrValues = new LinkedList<>();
1453
1454                        StringTokenizer st = new StringTokenizer(v, " ");
1455
1456                        while (st.hasMoreTokens()) {
1457
1458                                acrValues.add(new ACR(st.nextToken()));
1459                        }
1460                }
1461
1462
1463                v = params.get("claims");
1464
1465                ClaimsRequest claims = null;
1466
1467                if (StringUtils.isNotBlank(v)) {
1468
1469                        JSONObject jsonObject;
1470
1471                        try {
1472                                jsonObject = JSONObjectUtils.parse(v);
1473
1474                        } catch (ParseException e) {
1475                                String msg = "Invalid \"claims\" parameter: " + e.getMessage();
1476                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1477                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1478                        }
1479
1480                        // Parse exceptions silently ignored
1481                        claims = ClaimsRequest.parse(jsonObject);
1482                }
1483                
1484                
1485                v = params.get("request_uri");
1486                
1487                URI requestURI = null;
1488                
1489                if (StringUtils.isNotBlank(v)) {
1490
1491                        try {
1492                                requestURI = new URI(v);
1493                
1494                        } catch (URISyntaxException e) {
1495                                String msg = "Invalid \"request_uri\" parameter: " + e.getMessage();
1496                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1497                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1498                        }
1499                }
1500
1501                v = params.get("request");
1502
1503                JWT requestObject = null;
1504
1505                if (StringUtils.isNotBlank(v)) {
1506
1507                        // request_object and request_uri must not be defined at the same time
1508                        if (requestURI != null) {
1509                                String msg = "Invalid request: Found mutually exclusive \"request\" and \"request_uri\" parameters";
1510                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1511                                                         clientID, redirectURI, ar.impliedResponseMode(), state, null);
1512                        }
1513
1514                        try {
1515                                requestObject = JWTParser.parse(v);
1516                                
1517                        } catch (java.text.ParseException e) {
1518                                String msg = "Invalid \"request_object\" parameter: " + e.getMessage();
1519                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1520                                                         clientID, redirectURI, ar.impliedResponseMode(), state, e);
1521                        }
1522                }
1523
1524
1525                // Redirect URI required unless request_object / request_uri present
1526                if (redirectURI == null && requestObject == null && requestURI == null) {
1527                        String msg = "Missing \"redirect_uri\" parameter";
1528                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1529                                clientID, null, ar.impliedResponseMode(), state);
1530                }
1531
1532                // Parse additional custom parameters
1533                Map<String,String> customParams = null;
1534
1535                for (Map.Entry<String,String> p: params.entrySet()) {
1536
1537                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1538                                // We have a custom parameter
1539                                if (customParams == null) {
1540                                        customParams = new HashMap<>();
1541                                }
1542                                customParams.put(p.getKey(), p.getValue());
1543                        }
1544                }
1545
1546
1547                return new AuthenticationRequest(
1548                        uri, rt, rm, scope, clientID, redirectURI, state, nonce,
1549                        display, prompt, maxAge, uiLocales, claimsLocales,
1550                        idTokenHint, loginHint, acrValues, claims, requestObject, requestURI,
1551                        ar.getCodeChallenge(), ar.getCodeChallengeMethod(),
1552                        customParams);
1553        }
1554        
1555        
1556        /**
1557         * Parses an OpenID Connect authentication request from the specified
1558         * URI query string.
1559         *
1560         * <p>Example URI query string:
1561         *
1562         * <pre>
1563         * response_type=token%20id_token
1564         * &amp;client_id=s6BhdRkqt3
1565         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1566         * &amp;scope=openid%20profile
1567         * &amp;state=af0ifjsldkj
1568         * &amp;nonce=n-0S6_WzA2Mj
1569         * </pre>
1570         *
1571         * @param query The URI query string. Must not be {@code null}.
1572         *
1573         * @return The OpenID Connect authentication request.
1574         *
1575         * @throws ParseException If the query string couldn't be parsed to an 
1576         *                        OpenID Connect authentication request.
1577         */
1578        public static AuthenticationRequest parse(final String query)
1579                throws ParseException {
1580        
1581                return parse(null, URLUtils.parseParameters(query));
1582        }
1583
1584
1585        /**
1586         * Parses an OpenID Connect authentication request from the specified
1587         * URI query string.
1588         *
1589         * <p>Example URI query string:
1590         *
1591         * <pre>
1592         * response_type=token%20id_token
1593         * &amp;client_id=s6BhdRkqt3
1594         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1595         * &amp;scope=openid%20profile
1596         * &amp;state=af0ifjsldkj
1597         * &amp;nonce=n-0S6_WzA2Mj
1598         * </pre>
1599         *
1600         * @param uri   The URI of the OAuth 2.0 authorisation endpoint. May be
1601         *              {@code null} if the {@link #toHTTPRequest} method will
1602         *              not be used.
1603         * @param query The URI query string. Must not be {@code null}.
1604         *
1605         * @return The OpenID Connect authentication request.
1606         *
1607         * @throws ParseException If the query string couldn't be parsed to an
1608         *                        OpenID Connect authentication request.
1609         */
1610        public static AuthenticationRequest parse(final URI uri, final String query)
1611                throws ParseException {
1612
1613                return parse(uri, URLUtils.parseParameters(query));
1614        }
1615
1616
1617        /**
1618         * Parses an OpenID Connect authentication request from the specified
1619         * URI.
1620         *
1621         * <p>Example URI:
1622         *
1623         * <pre>
1624         * https://server.example.com/authorize?
1625         * response_type=token%20id_token
1626         * &amp;client_id=s6BhdRkqt3
1627         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1628         * &amp;scope=openid%20profile
1629         * &amp;state=af0ifjsldkj
1630         * &amp;nonce=n-0S6_WzA2Mj
1631         * </pre>
1632         *
1633         * @param uri The URI. Must not be {@code null}.
1634         *
1635         * @return The OpenID Connect authentication request.
1636         *
1637         * @throws ParseException If the query string couldn't be parsed to an
1638         *                        OpenID Connect authentication request.
1639         */
1640        public static AuthenticationRequest parse(final URI uri)
1641                throws ParseException {
1642
1643                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1644        }
1645        
1646        
1647        /**
1648         * Parses an authentication request from the specified HTTP GET or HTTP
1649         * POST request.
1650         *
1651         * <p>Example HTTP request (GET):
1652         *
1653         * <pre>
1654         * https://server.example.com/op/authorize?
1655         * response_type=code%20id_token
1656         * &amp;client_id=s6BhdRkqt3
1657         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1658         * &amp;scope=openid
1659         * &amp;nonce=n-0S6_WzA2Mj
1660         * &amp;state=af0ifjsldkj
1661         * </pre>
1662         *
1663         * @param httpRequest The HTTP request. Must not be {@code null}.
1664         *
1665         * @return The OpenID Connect authentication request.
1666         *
1667         * @throws ParseException If the HTTP request couldn't be parsed to an 
1668         *                        OpenID Connect authentication request.
1669         */
1670        public static AuthenticationRequest parse(final HTTPRequest httpRequest)
1671                throws ParseException {
1672                
1673                String query = httpRequest.getQuery();
1674                
1675                if (query == null)
1676                        throw new ParseException("Missing URI query string");
1677
1678                URI endpointURI;
1679
1680                try {
1681                        endpointURI = httpRequest.getURL().toURI();
1682
1683                } catch (URISyntaxException e) {
1684
1685                        throw new ParseException(e.getMessage(), e);
1686                }
1687                
1688                return parse(endpointURI, query);
1689        }
1690}