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