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