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                        // Nonce required in the implicit and hybrid flows
1275                        if (nonce == null && (rt.impliesImplicitFlow() || rt.impliesHybridFlow()))
1276                                throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow");
1277                }
1278                
1279                this.nonce = nonce;
1280                
1281                // Optional parameters
1282                this.display = display;
1283                this.maxAge = maxAge;
1284
1285                if (uiLocales != null)
1286                        this.uiLocales = Collections.unmodifiableList(uiLocales);
1287                else
1288                        this.uiLocales = null;
1289
1290                if (claimsLocales != null)
1291                        this.claimsLocales = Collections.unmodifiableList(claimsLocales);
1292                else
1293                        this.claimsLocales = null;
1294
1295                this.idTokenHint = idTokenHint;
1296                this.loginHint = loginHint;
1297
1298                if (acrValues != null)
1299                        this.acrValues = Collections.unmodifiableList(acrValues);
1300                else
1301                        this.acrValues = null;
1302
1303                this.claims = claims;
1304                
1305                if (purpose != null) {
1306                        if (purpose.length() < PURPOSE_MIN_LENGTH) {
1307                                throw new IllegalArgumentException("The purpose must not be shorter than " + PURPOSE_MIN_LENGTH + " characters");
1308                        }
1309                        if (purpose.length() > PURPOSE_MAX_LENGTH) {
1310                                throw new IllegalArgumentException("The purpose must not be longer than " + PURPOSE_MAX_LENGTH +" characters");
1311                        }
1312                }
1313                
1314                this.purpose = purpose;
1315        }
1316
1317
1318        /**
1319         * Returns the registered (standard) OpenID Connect authentication
1320         * request parameter names.
1321         *
1322         * @return The registered OpenID Connect authentication request
1323         *         parameter names, as a unmodifiable set.
1324         */
1325        public static Set<String> getRegisteredParameterNames() {
1326
1327                return REGISTERED_PARAMETER_NAMES;
1328        }
1329        
1330        
1331        /**
1332         * Gets the nonce. Corresponds to the conditionally optional 
1333         * {@code nonce} parameter.
1334         *
1335         * @return The nonce, {@code null} if not specified.
1336         */
1337        public Nonce getNonce() {
1338        
1339                return nonce;
1340        }
1341        
1342        
1343        /**
1344         * Gets the requested display type. Corresponds to the optional
1345         * {@code display} parameter.
1346         *
1347         * @return The requested display type, {@code null} if not specified.
1348         */
1349        public Display getDisplay() {
1350        
1351                return display;
1352        }
1353        
1354        
1355        /**
1356         * Gets the required maximum authentication age. Corresponds to the
1357         * optional {@code max_age} parameter.
1358         *
1359         * @return The maximum authentication age, in seconds; -1 if not
1360         *         specified, zero implies {@code prompt=login}.
1361         */
1362        public int getMaxAge() {
1363        
1364                return maxAge;
1365        }
1366
1367
1368        /**
1369         * Gets the end-user's preferred languages and scripts for the user
1370         * interface, ordered by preference. Corresponds to the optional
1371         * {@code ui_locales} parameter.
1372         *
1373         * @return The preferred UI locales, {@code null} if not specified.
1374         */
1375        public List<LangTag> getUILocales() {
1376
1377                return uiLocales;
1378        }
1379
1380
1381        /**
1382         * Gets the end-user's preferred languages and scripts for the claims
1383         * being returned, ordered by preference. Corresponds to the optional
1384         * {@code claims_locales} parameter.
1385         *
1386         * @return The preferred claims locales, {@code null} if not specified.
1387         */
1388        public List<LangTag> getClaimsLocales() {
1389
1390                return claimsLocales;
1391        }
1392
1393
1394        /**
1395         * Gets the ID Token hint. Corresponds to the conditionally optional 
1396         * {@code id_token_hint} parameter.
1397         *
1398         * @return The ID Token hint, {@code null} if not specified.
1399         */
1400        public JWT getIDTokenHint() {
1401        
1402                return idTokenHint;
1403        }
1404
1405
1406        /**
1407         * Gets the login hint. Corresponds to the optional {@code login_hint} 
1408         * parameter.
1409         *
1410         * @return The login hint, {@code null} if not specified.
1411         */
1412        public String getLoginHint() {
1413
1414                return loginHint;
1415        }
1416
1417
1418        /**
1419         * Gets the requested Authentication Context Class Reference values.
1420         * Corresponds to the optional {@code acr_values} parameter.
1421         *
1422         * @return The requested ACR values, {@code null} if not specified.
1423         */
1424        public List<ACR> getACRValues() {
1425
1426                return acrValues;
1427        }
1428
1429
1430        /**
1431         * Gets the individual claims to be returned. Corresponds to the 
1432         * optional {@code claims} parameter.
1433         *
1434         * @see #getOIDCClaims()
1435         *
1436         * @return The individual claims to be returned, {@code null} if not
1437         *         specified.
1438         */
1439        @Deprecated
1440        public ClaimsRequest getClaims() {
1441
1442                return toClaimsRequestWithSilentFail(claims);
1443        }
1444        
1445        
1446        private static OIDCClaimsRequest toOIDCClaimsRequestWithSilentFail(final ClaimsRequest claims) {
1447                if (claims == null) {
1448                        return null;
1449                }
1450                try {
1451                        return OIDCClaimsRequest.parse(claims.toJSONObject());
1452                } catch (ParseException e) {
1453                        return null;
1454                }
1455        }
1456        
1457        
1458        private static ClaimsRequest toClaimsRequestWithSilentFail(final OIDCClaimsRequest claims) {
1459                if (claims == null) {
1460                        return null;
1461                }
1462                try {
1463                        return ClaimsRequest.parse(claims.toJSONObject());
1464                } catch (ParseException e) {
1465                        return null;
1466                }
1467        }
1468
1469
1470        /**
1471         * Gets the individual OpenID claims to be returned. Corresponds to the
1472         * optional {@code claims} parameter.
1473         *
1474         * @return The individual claims to be returned, {@code null} if not
1475         *         specified.
1476         */
1477        public OIDCClaimsRequest getOIDCClaims() {
1478
1479                return claims;
1480        }
1481        
1482        
1483        /**
1484         * Gets the transaction specific purpose. Corresponds to the optional
1485         * {@code purpose} parameter.
1486         *
1487         * @return The purpose, {@code null} if not specified.
1488         */
1489        public String getPurpose() {
1490                
1491                return purpose;
1492        }
1493
1494
1495        @Override
1496        public Map<String,List<String>> toParameters() {
1497
1498                Map <String,List<String>> params = super.toParameters();
1499                
1500                if (nonce != null)
1501                        params.put("nonce", Collections.singletonList(nonce.toString()));
1502                
1503                if (display != null)
1504                        params.put("display", Collections.singletonList(display.toString()));
1505
1506                if (maxAge >= 0)
1507                        params.put("max_age", Collections.singletonList("" + maxAge));
1508
1509                if (uiLocales != null) {
1510
1511                        StringBuilder sb = new StringBuilder();
1512
1513                        for (LangTag locale: uiLocales) {
1514
1515                                if (sb.length() > 0)
1516                                        sb.append(' ');
1517
1518                                sb.append(locale.toString());
1519                        }
1520
1521                        params.put("ui_locales", Collections.singletonList(sb.toString()));
1522                }
1523
1524                if (claimsLocales != null) {
1525
1526                        StringBuilder sb = new StringBuilder();
1527
1528                        for (LangTag locale: claimsLocales) {
1529
1530                                if (sb.length() > 0)
1531                                        sb.append(' ');
1532
1533                                sb.append(locale.toString());
1534                        }
1535
1536                        params.put("claims_locales", Collections.singletonList(sb.toString()));
1537                }
1538
1539                if (idTokenHint != null) {
1540                
1541                        try {
1542                                params.put("id_token_hint", Collections.singletonList(idTokenHint.serialize()));
1543                                
1544                        } catch (IllegalStateException e) {
1545                        
1546                                throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e);
1547                        }
1548                }
1549
1550                if (loginHint != null)
1551                        params.put("login_hint", Collections.singletonList(loginHint));
1552
1553                if (acrValues != null) {
1554
1555                        StringBuilder sb = new StringBuilder();
1556
1557                        for (ACR acr: acrValues) {
1558
1559                                if (sb.length() > 0)
1560                                        sb.append(' ');
1561
1562                                sb.append(acr.toString());
1563                        }
1564
1565                        params.put("acr_values", Collections.singletonList(sb.toString()));
1566                }
1567                        
1568
1569                if (claims != null)
1570                        params.put("claims", Collections.singletonList(claims.toJSONObject().toString()));
1571                
1572                if (purpose != null)
1573                        params.put("purpose", Collections.singletonList(purpose));
1574
1575                return params;
1576        }
1577        
1578        
1579        @Override
1580        public JWTClaimsSet toJWTClaimsSet() {
1581                
1582                JWTClaimsSet jwtClaimsSet = super.toJWTClaimsSet();
1583                
1584                if (jwtClaimsSet.getClaim("max_age") != null) {
1585                        // Convert max_age to number in JSON object
1586                        try {
1587                                String maxAgeString = jwtClaimsSet.getStringClaim("max_age");
1588                                JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(jwtClaimsSet);
1589                                builder.claim("max_age", Integer.parseInt(maxAgeString));
1590                                return builder.build();
1591                        } catch (java.text.ParseException e) {
1592                                throw new SerializeException(e.getMessage());
1593                        }
1594                }
1595                
1596                return jwtClaimsSet;
1597        }
1598        
1599        
1600        /**
1601         * Parses an OpenID Connect authentication request from the specified
1602         * URI query parameters.
1603         *
1604         * <p>Example parameters:
1605         *
1606         * <pre>
1607         * response_type = token id_token
1608         * client_id     = s6BhdRkqt3
1609         * redirect_uri  = https://client.example.com/cb
1610         * scope         = openid profile
1611         * state         = af0ifjsldkj
1612         * nonce         = -0S6_WzA2Mj
1613         * </pre>
1614         *
1615         * @param params The parameters. Must not be {@code null}.
1616         *
1617         * @return The OpenID Connect authentication request.
1618         *
1619         * @throws ParseException If the parameters couldn't be parsed to an
1620         *                        OpenID Connect authentication request.
1621         */
1622        public static AuthenticationRequest parse(final Map<String,List<String>> params)
1623                throws ParseException {
1624
1625                return parse(null, params);
1626        }
1627
1628
1629        /**
1630         * Parses an OpenID Connect authentication request from the specified
1631         * URI and query parameters.
1632         *
1633         * <p>Example parameters:
1634         *
1635         * <pre>
1636         * response_type = token id_token
1637         * client_id     = s6BhdRkqt3
1638         * redirect_uri  = https://client.example.com/cb
1639         * scope         = openid profile
1640         * state         = af0ifjsldkj
1641         * nonce         = -0S6_WzA2Mj
1642         * </pre>
1643         *
1644         * @param uri    The URI of the OAuth 2.0 authorisation endpoint. May
1645         *               be {@code null} if the {@link #toHTTPRequest} method
1646         *               will not be used.
1647         * @param params The parameters. Must not be {@code null}.
1648         *
1649         * @return The OpenID Connect authentication request.
1650         *
1651         * @throws ParseException If the parameters couldn't be parsed to an
1652         *                        OpenID Connect authentication request.
1653         */
1654        public static AuthenticationRequest parse(final URI uri, final Map<String,List<String>> params)
1655                throws ParseException {
1656
1657                // Parse and validate the core OAuth 2.0 autz request params in 
1658                // the context of OIDC
1659                AuthorizationRequest ar = AuthorizationRequest.parse(uri, params);
1660                
1661                Nonce nonce = Nonce.parse(MultivaluedMapUtils.getFirstValue(params, "nonce"));
1662                
1663                if (! ar.specifiesRequestObject()) {
1664                        
1665                        // Required params if no JAR is present
1666                        
1667                        if (ar.getRedirectionURI() == null) {
1668                                String msg = "Missing \"redirect_uri\" parameter";
1669                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1670                                        ar.getClientID(), null, ar.impliedResponseMode(), ar.getState());
1671                        }
1672                        
1673                        if (ar.getScope() == null) {
1674                                String msg = "Missing \"scope\" parameter";
1675                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1676                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1677                        }
1678                        
1679                        // Nonce required in the implicit and hybrid flows
1680                        if (nonce == null && (ar.getResponseType().impliesImplicitFlow() || ar.getResponseType().impliesHybridFlow())) {
1681                                String msg = "Missing \"nonce\" parameter: Required in the implicit and hybrid flows";
1682                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1683                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1684                        }
1685                }
1686                
1687                // Check if present (not in JAR)
1688                if (ar.getResponseType() != null) {
1689                        try {
1690                                OIDCResponseTypeValidator.validate(ar.getResponseType());
1691                        } catch (IllegalArgumentException e) {
1692                                String msg = "Unsupported \"response_type\" parameter: " + e.getMessage();
1693                                throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg),
1694                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1695                        }
1696                }
1697                
1698                // Check if present (not in JAR)
1699                if (ar.getScope() != null && ! ar.getScope().contains(OIDCScopeValue.OPENID)) {
1700                        String msg = "The scope must include an \"openid\" value";
1701                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1702                                                 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1703                }
1704                
1705                Display display = null;
1706
1707                if (params.containsKey("display")) {
1708                        try {
1709                                display = Display.parse(MultivaluedMapUtils.getFirstValue(params, "display"));
1710
1711                        } catch (ParseException e) {
1712                                String msg = "Invalid \"display\" parameter: " + e.getMessage();
1713                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1714                                        ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1715                        }
1716                }
1717
1718
1719                String v = MultivaluedMapUtils.getFirstValue(params, "max_age");
1720
1721                int maxAge = -1;
1722
1723                if (StringUtils.isNotBlank(v)) {
1724
1725                        try {
1726                                maxAge = Integer.parseInt(v);
1727
1728                        } catch (NumberFormatException e) {
1729                                String msg = "Invalid \"max_age\" parameter: " + v;
1730                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1731                                                         ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1732                        }
1733                }
1734
1735
1736                v = MultivaluedMapUtils.getFirstValue(params, "ui_locales");
1737
1738                List<LangTag> uiLocales = null;
1739
1740                if (StringUtils.isNotBlank(v)) {
1741
1742                        uiLocales = new LinkedList<>();
1743
1744                        StringTokenizer st = new StringTokenizer(v, " ");
1745
1746                        while (st.hasMoreTokens()) {
1747
1748                                try {
1749                                        uiLocales.add(LangTag.parse(st.nextToken()));
1750
1751                                } catch (LangTagException e) {
1752                                        String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage();
1753                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1754                                                                 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1755                                }
1756                        }
1757                }
1758
1759
1760                v = MultivaluedMapUtils.getFirstValue(params, "claims_locales");
1761
1762                List<LangTag> claimsLocales = null;
1763
1764                if (StringUtils.isNotBlank(v)) {
1765
1766                        claimsLocales = new LinkedList<>();
1767
1768                        StringTokenizer st = new StringTokenizer(v, " ");
1769
1770                        while (st.hasMoreTokens()) {
1771
1772                                try {
1773                                        claimsLocales.add(LangTag.parse(st.nextToken()));
1774
1775                                } catch (LangTagException e) {
1776                                        String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage();
1777                                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1778                                                                 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1779                                }
1780                        }
1781                }
1782
1783
1784                v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint");
1785                
1786                JWT idTokenHint = null;
1787                
1788                if (StringUtils.isNotBlank(v)) {
1789                
1790                        try {
1791                                idTokenHint = JWTParser.parse(v);
1792                                
1793                        } catch (java.text.ParseException e) {
1794                                String msg = "Invalid \"id_token_hint\" 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                String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint");
1801
1802
1803                v = MultivaluedMapUtils.getFirstValue(params, "acr_values");
1804
1805                List<ACR> acrValues = null;
1806
1807                if (StringUtils.isNotBlank(v)) {
1808
1809                        acrValues = new LinkedList<>();
1810
1811                        StringTokenizer st = new StringTokenizer(v, " ");
1812
1813                        while (st.hasMoreTokens()) {
1814
1815                                acrValues.add(new ACR(st.nextToken()));
1816                        }
1817                }
1818
1819
1820                v = MultivaluedMapUtils.getFirstValue(params, "claims");
1821
1822                OIDCClaimsRequest claims = null;
1823
1824                if (StringUtils.isNotBlank(v)) {
1825                        try {
1826                                claims = OIDCClaimsRequest.parse(v);
1827                        } catch (ParseException e) {
1828                                String msg = "Invalid \"claims\" parameter: " + e.getMessage();
1829                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1830                                                         ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e);
1831                        }
1832                }
1833                
1834                String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose");
1835                
1836                if (purpose != null && (purpose.length() < PURPOSE_MIN_LENGTH || purpose.length() > PURPOSE_MAX_LENGTH)) {
1837                        String msg = "Invalid \"purpose\" parameter: Must not be shorter than " + PURPOSE_MIN_LENGTH + " and longer than " + PURPOSE_MAX_LENGTH + " characters";
1838                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1839                                ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState());
1840                }
1841                
1842
1843                // Parse additional custom parameters
1844                Map<String,List<String>> customParams = null;
1845
1846                for (Map.Entry<String,List<String>> p: params.entrySet()) {
1847
1848                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1849                                // We have a custom parameter
1850                                if (customParams == null) {
1851                                        customParams = new HashMap<>();
1852                                }
1853                                customParams.put(p.getKey(), p.getValue());
1854                        }
1855                }
1856
1857
1858                return new AuthenticationRequest(
1859                        uri, ar.getResponseType(), ar.getResponseMode(), ar.getScope(), ar.getClientID(), ar.getRedirectionURI(), ar.getState(), nonce,
1860                        display, ar.getPrompt(), maxAge, uiLocales, claimsLocales,
1861                        idTokenHint, loginHint, acrValues, claims, purpose,
1862                        ar.getRequestObject(), ar.getRequestURI(),
1863                        ar.getCodeChallenge(), ar.getCodeChallengeMethod(),
1864                        ar.getResources(),
1865                        ar.includeGrantedScopes(),
1866                        customParams);
1867        }
1868        
1869        
1870        /**
1871         * Parses an OpenID Connect authentication request from the specified
1872         * URI query string.
1873         *
1874         * <p>Example URI query string:
1875         *
1876         * <pre>
1877         * response_type=token%20id_token
1878         * &amp;client_id=s6BhdRkqt3
1879         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1880         * &amp;scope=openid%20profile
1881         * &amp;state=af0ifjsldkj
1882         * &amp;nonce=n-0S6_WzA2Mj
1883         * </pre>
1884         *
1885         * @param query The URI query string. Must not be {@code null}.
1886         *
1887         * @return The OpenID Connect authentication request.
1888         *
1889         * @throws ParseException If the query string couldn't be parsed to an 
1890         *                        OpenID Connect authentication request.
1891         */
1892        public static AuthenticationRequest parse(final String query)
1893                throws ParseException {
1894        
1895                return parse(null, URLUtils.parseParameters(query));
1896        }
1897
1898
1899        /**
1900         * Parses an OpenID Connect authentication request from the specified
1901         * URI query string.
1902         *
1903         * <p>Example URI query string:
1904         *
1905         * <pre>
1906         * response_type=token%20id_token
1907         * &amp;client_id=s6BhdRkqt3
1908         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1909         * &amp;scope=openid%20profile
1910         * &amp;state=af0ifjsldkj
1911         * &amp;nonce=n-0S6_WzA2Mj
1912         * </pre>
1913         *
1914         * @param uri   The URI of the OAuth 2.0 authorisation endpoint. May be
1915         *              {@code null} if the {@link #toHTTPRequest} method will
1916         *              not be used.
1917         * @param query The URI query string. Must not be {@code null}.
1918         *
1919         * @return The OpenID Connect authentication request.
1920         *
1921         * @throws ParseException If the query string couldn't be parsed to an
1922         *                        OpenID Connect authentication request.
1923         */
1924        public static AuthenticationRequest parse(final URI uri, final String query)
1925                throws ParseException {
1926
1927                return parse(uri, URLUtils.parseParameters(query));
1928        }
1929
1930
1931        /**
1932         * Parses an OpenID Connect authentication request from the specified
1933         * URI.
1934         *
1935         * <p>Example URI:
1936         *
1937         * <pre>
1938         * https://server.example.com/authorize?
1939         * response_type=token%20id_token
1940         * &amp;client_id=s6BhdRkqt3
1941         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1942         * &amp;scope=openid%20profile
1943         * &amp;state=af0ifjsldkj
1944         * &amp;nonce=n-0S6_WzA2Mj
1945         * </pre>
1946         *
1947         * @param uri The URI. Must not be {@code null}.
1948         *
1949         * @return The OpenID Connect authentication request.
1950         *
1951         * @throws ParseException If the query string couldn't be parsed to an
1952         *                        OpenID Connect authentication request.
1953         */
1954        public static AuthenticationRequest parse(final URI uri)
1955                throws ParseException {
1956
1957                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1958        }
1959        
1960        
1961        /**
1962         * Parses an authentication request from the specified HTTP GET or HTTP
1963         * POST request.
1964         *
1965         * <p>Example HTTP request (GET):
1966         *
1967         * <pre>
1968         * https://server.example.com/op/authorize?
1969         * response_type=code%20id_token
1970         * &amp;client_id=s6BhdRkqt3
1971         * &amp;redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
1972         * &amp;scope=openid
1973         * &amp;nonce=n-0S6_WzA2Mj
1974         * &amp;state=af0ifjsldkj
1975         * </pre>
1976         *
1977         * @param httpRequest The HTTP request. Must not be {@code null}.
1978         *
1979         * @return The OpenID Connect authentication request.
1980         *
1981         * @throws ParseException If the HTTP request couldn't be parsed to an 
1982         *                        OpenID Connect authentication request.
1983         */
1984        public static AuthenticationRequest parse(final HTTPRequest httpRequest)
1985                throws ParseException {
1986                
1987                String query = httpRequest.getQuery();
1988                
1989                if (query == null)
1990                        throw new ParseException("Missing URI query string");
1991
1992                URI endpointURI;
1993
1994                try {
1995                        endpointURI = httpRequest.getURL().toURI();
1996
1997                } catch (URISyntaxException e) {
1998
1999                        throw new ParseException(e.getMessage(), e);
2000                }
2001                
2002                return parse(endpointURI, query);
2003        }
2004}