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                        params.put("ui_locales", Collections.singletonList(LangTagUtils.concat(uiLocales)));
1527                }
1528
1529                if (CollectionUtils.isNotEmpty(claimsLocales)) {
1530                        params.put("claims_locales", Collections.singletonList(LangTagUtils.concat(claimsLocales)));
1531                }
1532
1533                if (idTokenHint != null) {
1534                
1535                        try {
1536                                params.put("id_token_hint", Collections.singletonList(idTokenHint.serialize()));
1537                                
1538                        } catch (IllegalStateException e) {
1539                        
1540                                throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e);
1541                        }
1542                }
1543
1544                if (loginHint != null)
1545                        params.put("login_hint", Collections.singletonList(loginHint));
1546
1547                if (acrValues != null) {
1548
1549                        StringBuilder sb = new StringBuilder();
1550
1551                        for (ACR acr: acrValues) {
1552
1553                                if (sb.length() > 0)
1554                                        sb.append(' ');
1555
1556                                sb.append(acr.toString());
1557                        }
1558
1559                        params.put("acr_values", Collections.singletonList(sb.toString()));
1560                }
1561                        
1562
1563                if (claims != null)
1564                        params.put("claims", Collections.singletonList(claims.toJSONObject().toString()));
1565                
1566                if (purpose != null)
1567                        params.put("purpose", Collections.singletonList(purpose));
1568
1569                return params;
1570        }
1571        
1572        
1573        @Override
1574        public JWTClaimsSet toJWTClaimsSet() {
1575                
1576                JWTClaimsSet jwtClaimsSet = super.toJWTClaimsSet();
1577                
1578                if (jwtClaimsSet.getClaim("max_age") != null) {
1579                        // Convert max_age to number in JSON object
1580                        try {
1581                                String maxAgeString = jwtClaimsSet.getStringClaim("max_age");
1582                                JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(jwtClaimsSet);
1583                                builder.claim("max_age", Integer.parseInt(maxAgeString));
1584                                return builder.build();
1585                        } catch (java.text.ParseException e) {
1586                                throw new SerializeException(e.getMessage());
1587                        }
1588                }
1589                
1590                return jwtClaimsSet;
1591        }
1592        
1593        
1594        /**
1595         * Parses an OpenID Connect authentication request from the specified
1596         * URI query parameters.
1597         *
1598         * <p>Example parameters:
1599         *
1600         * <pre>
1601         * response_type = token id_token
1602         * client_id     = s6BhdRkqt3
1603         * redirect_uri  = https://client.example.com/cb
1604         * scope         = openid profile
1605         * state         = af0ifjsldkj
1606         * nonce         = -0S6_WzA2Mj
1607         * </pre>
1608         *
1609         * @param params The parameters. Must not be {@code null}.
1610         *
1611         * @return The OpenID Connect authentication request.
1612         *
1613         * @throws ParseException If the parameters couldn't be parsed to an
1614         *                        OpenID Connect authentication request.
1615         */
1616        public static AuthenticationRequest parse(final Map<String,List<String>> params)
1617                throws ParseException {
1618
1619                return parse(null, params);
1620        }
1621
1622
1623        /**
1624         * Parses an OpenID Connect authentication request from the specified
1625         * URI and query parameters.
1626         *
1627         * <p>Example parameters:
1628         *
1629         * <pre>
1630         * response_type = token id_token
1631         * client_id     = s6BhdRkqt3
1632         * redirect_uri  = https://client.example.com/cb
1633         * scope         = openid profile
1634         * state         = af0ifjsldkj
1635         * nonce         = -0S6_WzA2Mj
1636         * </pre>
1637         *
1638         * @param uri    The URI of the OAuth 2.0 authorisation endpoint. May
1639         *               be {@code null} if the {@link #toHTTPRequest} method
1640         *               will not be used.
1641         * @param params The parameters. Must not be {@code null}.
1642         *
1643         * @return The OpenID Connect authentication request.
1644         *
1645         * @throws ParseException If the parameters couldn't be parsed to an
1646         *                        OpenID Connect authentication request.
1647         */
1648        public static AuthenticationRequest parse(final URI uri, final Map<String,List<String>> params)
1649                throws ParseException {
1650
1651                // Parse and validate the core OAuth 2.0 autz request params in 
1652                // the context of OIDC
1653                AuthorizationRequest ar = AuthorizationRequest.parse(uri, params);
1654                
1655                Nonce nonce = Nonce.parse(MultivaluedMapUtils.getFirstValue(params, "nonce"));
1656                
1657                if (! ar.specifiesRequestObject()) {
1658                        
1659                        // Required params if no JAR is present
1660                        
1661                        if (ar.getRedirectionURI() == null) {
1662                                String msg = "Missing redirect_uri parameter";
1663                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1664                                        ar.getClientID(), null, ar.impliedResponseMode(), ar.getState());
1665                        }
1666                        
1667                        if (ar.getScope() == null) {
1668                                String msg = "Missing scope parameter";
1669                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1670                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1671                        }
1672                        
1673                        // Check nonce requirement
1674                        if (nonce == null && Nonce.isRequired(ar.getResponseType())) {
1675                                String msg = "Missing nonce parameter: Required for response_type=" + ar.getResponseType();
1676                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1677                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1678                        }
1679                }
1680                
1681                // Check if present (not in JAR)
1682                if (ar.getResponseType() != null) {
1683                        try {
1684                                OIDCResponseTypeValidator.validate(ar.getResponseType());
1685                        } catch (IllegalArgumentException e) {
1686                                String msg = "Unsupported response_type parameter: " + e.getMessage();
1687                                throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg),
1688                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1689                        }
1690                }
1691                
1692                // Check if present (not in JAR)
1693                if (ar.getScope() != null && ! ar.getScope().contains(OIDCScopeValue.OPENID)) {
1694                        String msg = "The scope must include an openid value";
1695                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1696                                                 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1697                }
1698                
1699                Display display = null;
1700
1701                if (params.containsKey("display")) {
1702                        try {
1703                                display = Display.parse(MultivaluedMapUtils.getFirstValue(params, "display"));
1704
1705                        } catch (ParseException e) {
1706                                String msg = "Invalid display parameter: " + e.getMessage();
1707                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1708                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1709                        }
1710                }
1711
1712
1713                String v = MultivaluedMapUtils.getFirstValue(params, "max_age");
1714
1715                int maxAge = -1;
1716
1717                if (StringUtils.isNotBlank(v)) {
1718
1719                        try {
1720                                maxAge = Integer.parseInt(v);
1721
1722                        } catch (NumberFormatException e) {
1723                                String msg = "Invalid max_age parameter: " + v;
1724                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1725                                                         ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1726                        }
1727                }
1728
1729
1730                List<LangTag> uiLocales;
1731                try {
1732                        uiLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "ui_locales"));
1733                } catch (LangTagException e) {
1734                        String msg = "Invalid ui_locales parameter: " + e.getMessage();
1735                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1736                                ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1737                }
1738
1739
1740                List<LangTag> claimsLocales;
1741                try {
1742                        claimsLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "claims_locales"));
1743                        
1744                } catch (LangTagException e) {
1745                        String msg = "Invalid claims_locales parameter: " + e.getMessage();
1746                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1747                                ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1748                }
1749
1750
1751                v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint");
1752                
1753                JWT idTokenHint = null;
1754                
1755                if (StringUtils.isNotBlank(v)) {
1756                
1757                        try {
1758                                idTokenHint = JWTParser.parse(v);
1759                                
1760                        } catch (java.text.ParseException e) {
1761                                String msg = "Invalid id_token_hint parameter: " + e.getMessage();
1762                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1763                                                         ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1764                        }
1765                }
1766
1767                String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint");
1768
1769
1770                v = MultivaluedMapUtils.getFirstValue(params, "acr_values");
1771
1772                List<ACR> acrValues = null;
1773
1774                if (StringUtils.isNotBlank(v)) {
1775
1776                        acrValues = new LinkedList<>();
1777
1778                        StringTokenizer st = new StringTokenizer(v, " ");
1779
1780                        while (st.hasMoreTokens()) {
1781
1782                                acrValues.add(new ACR(st.nextToken()));
1783                        }
1784                }
1785
1786
1787                v = MultivaluedMapUtils.getFirstValue(params, "claims");
1788
1789                OIDCClaimsRequest claims = null;
1790
1791                if (StringUtils.isNotBlank(v)) {
1792                        try {
1793                                claims = OIDCClaimsRequest.parse(v);
1794                        } catch (ParseException e) {
1795                                String msg = "Invalid claims parameter: " + e.getMessage();
1796                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1797                                                         ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1798                        }
1799                }
1800                
1801                String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose");
1802                
1803                if (purpose != null && (purpose.length() < PURPOSE_MIN_LENGTH || purpose.length() > PURPOSE_MAX_LENGTH)) {
1804                        String msg = "Invalid purpose parameter: Must not be shorter than " + PURPOSE_MIN_LENGTH + " and longer than " + PURPOSE_MAX_LENGTH + " characters";
1805                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1806                                ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1807                }
1808                
1809
1810                // Parse additional custom parameters
1811                Map<String,List<String>> customParams = null;
1812
1813                for (Map.Entry<String,List<String>> p: params.entrySet()) {
1814
1815                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1816                                // We have a custom parameter
1817                                if (customParams == null) {
1818                                        customParams = new HashMap<>();
1819                                }
1820                                customParams.put(p.getKey(), p.getValue());
1821                        }
1822                }
1823
1824
1825                return new AuthenticationRequest(
1826                        uri, ar.getResponseType(), ar.getResponseMode(), ar.getScope(), ar.getClientID(), ar.getRedirectionURI(), ar.getState(), nonce,
1827                        display, ar.getPrompt(), maxAge, uiLocales, claimsLocales,
1828                        idTokenHint, loginHint, acrValues, claims, purpose,
1829                        ar.getRequestObject(), ar.getRequestURI(),
1830                        ar.getCodeChallenge(), ar.getCodeChallengeMethod(),
1831                        ar.getResources(),
1832                        ar.includeGrantedScopes(),
1833                        customParams);
1834        }
1835        
1836        
1837        /**
1838         * Parses an OpenID Connect authentication request from the specified
1839         * URI query string.
1840         *
1841         * <p>Example URI query string:
1842         *
1843         * <pre>
1844         * response_type=token%20id_token
1845         * &amp;client_id=s6BhdRkqt3
1846         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1847         * &amp;scope=openid%20profile
1848         * &amp;state=af0ifjsldkj
1849         * &amp;nonce=n-0S6_WzA2Mj
1850         * </pre>
1851         *
1852         * @param query The URI query string. Must not be {@code null}.
1853         *
1854         * @return The OpenID Connect authentication request.
1855         *
1856         * @throws ParseException If the query string couldn't be parsed to an 
1857         *                        OpenID Connect authentication request.
1858         */
1859        public static AuthenticationRequest parse(final String query)
1860                throws ParseException {
1861        
1862                return parse(null, URLUtils.parseParameters(query));
1863        }
1864
1865
1866        /**
1867         * Parses an OpenID Connect authentication request from the specified
1868         * URI query string.
1869         *
1870         * <p>Example URI query string:
1871         *
1872         * <pre>
1873         * response_type=token%20id_token
1874         * &amp;client_id=s6BhdRkqt3
1875         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1876         * &amp;scope=openid%20profile
1877         * &amp;state=af0ifjsldkj
1878         * &amp;nonce=n-0S6_WzA2Mj
1879         * </pre>
1880         *
1881         * @param uri   The URI of the OAuth 2.0 authorisation endpoint. May be
1882         *              {@code null} if the {@link #toHTTPRequest} method will
1883         *              not be used.
1884         * @param query The URI query string. Must not be {@code null}.
1885         *
1886         * @return The OpenID Connect authentication request.
1887         *
1888         * @throws ParseException If the query string couldn't be parsed to an
1889         *                        OpenID Connect authentication request.
1890         */
1891        public static AuthenticationRequest parse(final URI uri, final String query)
1892                throws ParseException {
1893
1894                return parse(uri, URLUtils.parseParameters(query));
1895        }
1896
1897
1898        /**
1899         * Parses an OpenID Connect authentication request from the specified
1900         * URI.
1901         *
1902         * <p>Example URI:
1903         *
1904         * <pre>
1905         * https://server.example.com/authorize?
1906         * response_type=token%20id_token
1907         * &amp;client_id=s6BhdRkqt3
1908         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1909         * &amp;scope=openid%20profile
1910         * &amp;state=af0ifjsldkj
1911         * &amp;nonce=n-0S6_WzA2Mj
1912         * </pre>
1913         *
1914         * @param uri The URI. Must not be {@code null}.
1915         *
1916         * @return The OpenID Connect authentication request.
1917         *
1918         * @throws ParseException If the query string couldn't be parsed to an
1919         *                        OpenID Connect authentication request.
1920         */
1921        public static AuthenticationRequest parse(final URI uri)
1922                throws ParseException {
1923
1924                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1925        }
1926        
1927        
1928        /**
1929         * Parses an authentication request from the specified HTTP GET or HTTP
1930         * POST request.
1931         *
1932         * <p>Example HTTP request (GET):
1933         *
1934         * <pre>
1935         * https://server.example.com/op/authorize?
1936         * response_type=code%20id_token
1937         * &amp;client_id=s6BhdRkqt3
1938         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1939         * &amp;scope=openid
1940         * &amp;nonce=n-0S6_WzA2Mj
1941         * &amp;state=af0ifjsldkj
1942         * </pre>
1943         *
1944         * @param httpRequest The HTTP request. Must not be {@code null}.
1945         *
1946         * @return The OpenID Connect authentication request.
1947         *
1948         * @throws ParseException If the HTTP request couldn't be parsed to an 
1949         *                        OpenID Connect authentication request.
1950         */
1951        public static AuthenticationRequest parse(final HTTPRequest httpRequest)
1952                throws ParseException {
1953                
1954                String query = httpRequest.getQuery();
1955                
1956                if (query == null)
1957                        throw new ParseException("Missing URI query string");
1958
1959                URI endpointURI = httpRequest.getURI();
1960                
1961                return parse(endpointURI, query);
1962        }
1963}