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