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