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