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