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 requested prompt. Corresponds to the optional
653                 * {@code prompt} parameter.
654                 *
655                 * @param promptType The requested prompt types, {@code null}
656                 *                   if not specified.
657                 *
658                 * @return This builder.
659                 */
660                public Builder prompt(final Prompt.Type ... promptType) {
661                        if (promptType.length == 1 && promptType[0] == null) {
662                                return prompt((Prompt)null);
663                        } else {
664                                return prompt(new Prompt(promptType));
665                        }
666                }
667                
668                
669                /**
670                 * Sets the DPoP JWK SHA-256 thumbprint. Corresponds to the
671                 * optional {@code dpop_jkt} parameter.
672                 *
673                 * @param dpopJKT DPoP JWK SHA-256 thumbprint, {@code null} if
674                 *                not specified.
675                 *
676                 * @return This builder.
677                 */
678                public Builder dPoPJWKThumbprintConfirmation(final JWKThumbprintConfirmation dpopJKT) {
679                        this.dpopJKT = dpopJKT;
680                        return this;
681                }
682                
683                
684                /**
685                 * Sets the OpenID Connect Federation 1.0 trust chain.
686                 * Corresponds to the optional {@code trust_chain} parameter.
687                 *
688                 * @param trustChain The trust chain, {@code null} if not
689                 *                   specified.
690                 *
691                 * @return This builder.
692                 */
693                public Builder trustChain(final TrustChain trustChain) {
694                        this.trustChain = trustChain;
695                        return this;
696                }
697                
698                
699                /**
700                 * Sets a custom parameter.
701                 *
702                 * @param name   The parameter name. Must not be {@code null}.
703                 * @param values The parameter values, {@code null} if not
704                 *               specified.
705                 *
706                 * @return This builder.
707                 */
708                public Builder customParameter(final String name, final String ... values) {
709                        if (values == null || values.length == 0) {
710                                customParams.remove(name);
711                        } else {
712                                customParams.put(name, Arrays.asList(values));
713                        }
714                        return this;
715                }
716
717
718                /**
719                 * Sets the URI of the endpoint (HTTP or HTTPS) for which the
720                 * request is intended.
721                 *
722                 * @param uri The endpoint URI, {@code null} if not specified.
723                 *
724                 * @return This builder.
725                 */
726                public Builder endpointURI(final URI uri) {
727                        this.uri = uri;
728                        return this;
729                }
730
731
732                /**
733                 * Builds a new authorisation request.
734                 *
735                 * @return The authorisation request.
736                 */
737                public AuthorizationRequest build() {
738                        try {
739                                return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state,
740                                        codeChallenge, codeChallengeMethod, resources, includeGrantedScopes,
741                                        requestObject, requestURI,
742                                        prompt, dpopJKT, trustChain,
743                                        customParams);
744                        } catch (IllegalArgumentException e) {
745                                throw new IllegalStateException(e.getMessage(), e);
746                        }
747                }
748        }
749
750
751        /**
752         * Creates a new minimal authorisation request.
753         *
754         * @param uri      The URI of the authorisation endpoint. May be
755         *                 {@code null} if the {@link #toHTTPRequest} method
756         *                 will not be used.
757         * @param rt       The response type. Corresponds to the
758         *                 {@code response_type} parameter. Must not be
759         *                 {@code null}.
760         * @param clientID The client identifier. Corresponds to the
761         *                 {@code client_id} parameter. Must not be
762         *                 {@code null}.
763         */
764        public AuthorizationRequest(final URI uri,
765                                    final ResponseType rt,
766                                    final ClientID clientID) {
767
768                this(uri, rt, null, clientID, null, null, null, null, null, null, false, null, null, null, null);
769        }
770
771
772        /**
773         * Creates a new authorisation request.
774         *
775         * @param uri                 The URI of the authorisation endpoint.
776         *                            May be {@code null} if the
777         *                            {@link #toHTTPRequest} method will not be
778         *                            used.
779         * @param rt                  The response type. Corresponds to the
780         *                            {@code response_type} parameter. Must not
781         *                            be {@code null}.
782         * @param rm                  The response mode. Corresponds to the
783         *                            optional {@code response_mode} parameter.
784         *                            Use of this parameter is not recommended
785         *                            unless a non-default response mode is
786         *                            requested (e.g. form_post).
787         * @param clientID            The client identifier. Corresponds to the
788         *                            {@code client_id} parameter. Must not be
789         *                            {@code null}.
790         * @param redirectURI         The redirection URI. Corresponds to the
791         *                            optional {@code redirect_uri} parameter.
792         *                            {@code null} if not specified.
793         * @param scope               The request scope. Corresponds to the
794         *                            optional {@code scope} parameter.
795         *                            {@code null} if not specified.
796         * @param state               The state. Corresponds to the recommended
797         *                            {@code state} parameter. {@code null} if
798         *                            not specified.
799         */
800        public AuthorizationRequest(final URI uri,
801                                    final ResponseType rt,
802                                    final ResponseMode rm,
803                                    final ClientID clientID,
804                                    final URI redirectURI,
805                                    final Scope scope,
806                                    final State state) {
807
808                this(uri, rt, rm, clientID, redirectURI, scope, state, null, null, null, false, null, null, null, null);
809        }
810
811
812        /**
813         * Creates a new authorisation request with extension and custom
814         * parameters.
815         *
816         * @param uri                  The URI of the authorisation endpoint.
817         *                             May be {@code null} if the
818         *                             {@link #toHTTPRequest} method will not
819         *                             be used.
820         * @param rt                   The response type. Corresponds to the
821         *                             {@code response_type} parameter. Must
822         *                             not be {@code null}, unless a request a
823         *                             request object or URI is specified.
824         * @param rm                   The response mode. Corresponds to the
825         *                             optional {@code response_mode}
826         *                             parameter. Use of this parameter is not
827         *                             recommended unless a non-default
828         *                             response mode is requested (e.g.
829         *                             form_post).
830         * @param clientID             The client identifier. Corresponds to
831         *                             the {@code client_id} parameter. Must
832         *                             not be {@code null}, unless a request
833         *                             object or URI is specified.
834         * @param redirectURI          The redirection URI. Corresponds to the
835         *                             optional {@code redirect_uri} parameter.
836         *                             {@code null} if not specified.
837         * @param scope                The request scope. Corresponds to the
838         *                             optional {@code scope} parameter.
839         *                             {@code null} if not specified.
840         * @param state                The state. Corresponds to the
841         *                             recommended {@code state} parameter.
842         *                             {@code null} if not specified.
843         * @param codeChallenge        The code challenge for PKCE,
844         *                             {@code null} if not specified.
845         * @param codeChallengeMethod  The code challenge method for PKCE,
846         *                             {@code null} if not specified.
847         * @param resources            The resource URI(s), {@code null} if not
848         *                             specified.
849         * @param includeGrantedScopes {@code true} to request incremental
850         *                             authorisation.
851         * @param requestObject        The request object. Corresponds to the
852         *                             optional {@code request} parameter. Must
853         *                             not be specified together with a request
854         *                             object URI. {@code null} if not
855         *                             specified.
856         * @param requestURI           The request object URI. Corresponds to
857         *                             the optional {@code request_uri}
858         *                             parameter. Must not be specified
859         *                             together with a request object.
860         *                             {@code null} if not specified.
861         * @param prompt               The requested prompt. Corresponds to the
862         *                             optional {@code prompt} parameter.
863         * @param customParams         Custom parameters, empty map or
864         *                             {@code null} if none.
865         */
866        @Deprecated
867        public AuthorizationRequest(final URI uri,
868                                    final ResponseType rt,
869                                    final ResponseMode rm,
870                                    final ClientID clientID,
871                                    final URI redirectURI,
872                                    final Scope scope,
873                                    final State state,
874                                    final CodeChallenge codeChallenge,
875                                    final CodeChallengeMethod codeChallengeMethod,
876                                    final List<URI> resources,
877                                    final boolean includeGrantedScopes,
878                                    final JWT requestObject,
879                                    final URI requestURI,
880                                    final Prompt prompt,
881                                    final Map<String, List<String>> customParams) {
882
883                this(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, resources, includeGrantedScopes, requestObject, requestURI, prompt, null, customParams);
884        }
885
886
887        /**
888         * Creates a new authorisation request with extension and custom
889         * parameters.
890         *
891         * @param uri                  The URI of the authorisation endpoint.
892         *                             May be {@code null} if the
893         *                             {@link #toHTTPRequest} method will not
894         *                             be used.
895         * @param rt                   The response type. Corresponds to the
896         *                             {@code response_type} parameter. Must
897         *                             not be {@code null}, unless a request a
898         *                             request object or URI is specified.
899         * @param rm                   The response mode. Corresponds to the
900         *                             optional {@code response_mode}
901         *                             parameter. Use of this parameter is not
902         *                             recommended unless a non-default
903         *                             response mode is requested (e.g.
904         *                             form_post).
905         * @param clientID             The client identifier. Corresponds to
906         *                             the {@code client_id} parameter. Must
907         *                             not be {@code null}, unless a request
908         *                             object or URI is specified.
909         * @param redirectURI          The redirection URI. Corresponds to the
910         *                             optional {@code redirect_uri} parameter.
911         *                             {@code null} if not specified.
912         * @param scope                The request scope. Corresponds to the
913         *                             optional {@code scope} parameter.
914         *                             {@code null} if not specified.
915         * @param state                The state. Corresponds to the
916         *                             recommended {@code state} parameter.
917         *                             {@code null} if not specified.
918         * @param codeChallenge        The code challenge for PKCE,
919         *                             {@code null} if not specified.
920         * @param codeChallengeMethod  The code challenge method for PKCE,
921         *                             {@code null} if not specified.
922         * @param resources            The resource URI(s), {@code null} if not
923         *                             specified.
924         * @param includeGrantedScopes {@code true} to request incremental
925         *                             authorisation.
926         * @param requestObject        The request object. Corresponds to the
927         *                             optional {@code request} parameter. Must
928         *                             not be specified together with a request
929         *                             object URI. {@code null} if not
930         *                             specified.
931         * @param requestURI           The request object URI. Corresponds to
932         *                             the optional {@code request_uri}
933         *                             parameter. Must not be specified
934         *                             together with a request object.
935         *                             {@code null} if not specified.
936         * @param prompt               The requested prompt. Corresponds to the
937         *                             optional {@code prompt} parameter.
938         * @param dpopJKT              The DPoP JWK SHA-256 thumbprint,
939         *                             {@code null} if not specified.
940         * @param customParams         Custom parameters, empty map or
941         *                             {@code null} if none.
942         */
943        @Deprecated
944        public AuthorizationRequest(final URI uri,
945                                    final ResponseType rt,
946                                    final ResponseMode rm,
947                                    final ClientID clientID,
948                                    final URI redirectURI,
949                                    final Scope scope,
950                                    final State state,
951                                    final CodeChallenge codeChallenge,
952                                    final CodeChallengeMethod codeChallengeMethod,
953                                    final List<URI> resources,
954                                    final boolean includeGrantedScopes,
955                                    final JWT requestObject,
956                                    final URI requestURI,
957                                    final Prompt prompt,
958                                    final JWKThumbprintConfirmation dpopJKT,
959                                    final Map<String, List<String>> customParams) {
960                
961                this(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, resources, includeGrantedScopes, requestObject, requestURI, prompt, dpopJKT, null, customParams);
962        }
963
964
965        /**
966         * Creates a new authorisation request with extension and custom
967         * parameters.
968         *
969         * @param uri                  The URI of the authorisation endpoint.
970         *                             May be {@code null} if the
971         *                             {@link #toHTTPRequest} method will not
972         *                             be used.
973         * @param rt                   The response type. Corresponds to the
974         *                             {@code response_type} parameter. Must
975         *                             not be {@code null}, unless a request a
976         *                             request object or URI is specified.
977         * @param rm                   The response mode. Corresponds to the
978         *                             optional {@code response_mode}
979         *                             parameter. Use of this parameter is not
980         *                             recommended unless a non-default
981         *                             response mode is requested (e.g.
982         *                             form_post).
983         * @param clientID             The client identifier. Corresponds to
984         *                             the {@code client_id} parameter. Must
985         *                             not be {@code null}, unless a request
986         *                             object or URI is specified.
987         * @param redirectURI          The redirection URI. Corresponds to the
988         *                             optional {@code redirect_uri} parameter.
989         *                             {@code null} if not specified.
990         * @param scope                The request scope. Corresponds to the
991         *                             optional {@code scope} parameter.
992         *                             {@code null} if not specified.
993         * @param state                The state. Corresponds to the
994         *                             recommended {@code state} parameter.
995         *                             {@code null} if not specified.
996         * @param codeChallenge        The code challenge for PKCE,
997         *                             {@code null} if not specified.
998         * @param codeChallengeMethod  The code challenge method for PKCE,
999         *                             {@code null} if not specified.
1000         * @param resources            The resource URI(s), {@code null} if not
1001         *                             specified.
1002         * @param includeGrantedScopes {@code true} to request incremental
1003         *                             authorisation.
1004         * @param requestObject        The request object. Corresponds to the
1005         *                             optional {@code request} parameter. Must
1006         *                             not be specified together with a request
1007         *                             object URI. {@code null} if not
1008         *                             specified.
1009         * @param requestURI           The request object URI. Corresponds to
1010         *                             the optional {@code request_uri}
1011         *                             parameter. Must not be specified
1012         *                             together with a request object.
1013         *                             {@code null} if not specified.
1014         * @param prompt               The requested prompt. Corresponds to the
1015         *                             optional {@code prompt} parameter.
1016         * @param dpopJKT              The DPoP JWK SHA-256 thumbprint,
1017         *                             {@code null} if not specified.
1018         * @param trustChain           The OpenID Connect Federation 1.0 trust
1019         *                             chain, {@code null} if not specified.
1020         * @param customParams         Custom parameters, empty map or
1021         *                             {@code null} if none.
1022         */
1023        public AuthorizationRequest(final URI uri,
1024                                    final ResponseType rt,
1025                                    final ResponseMode rm,
1026                                    final ClientID clientID,
1027                                    final URI redirectURI,
1028                                    final Scope scope,
1029                                    final State state,
1030                                    final CodeChallenge codeChallenge,
1031                                    final CodeChallengeMethod codeChallengeMethod,
1032                                    final List<URI> resources,
1033                                    final boolean includeGrantedScopes,
1034                                    final JWT requestObject,
1035                                    final URI requestURI,
1036                                    final Prompt prompt,
1037                                    final JWKThumbprintConfirmation dpopJKT,
1038                                    final TrustChain trustChain,
1039                                    final Map<String, List<String>> customParams) {
1040
1041                super(uri);
1042
1043                if (rt == null && requestObject == null && requestURI == null)
1044                        throw new IllegalArgumentException("The response type must not be null");
1045
1046                this.rt = rt;
1047
1048                this.rm = rm;
1049
1050
1051                if (clientID == null)
1052                        throw new IllegalArgumentException("The client ID must not be null");
1053
1054                this.clientID = clientID;
1055
1056
1057                this.redirectURI = redirectURI;
1058                this.scope = scope;
1059                this.state = state;
1060
1061                this.codeChallenge = codeChallenge;
1062                this.codeChallengeMethod = codeChallengeMethod;
1063                
1064                this.resources = ResourceUtils.ensureLegalResourceURIs(resources);
1065                
1066                this.includeGrantedScopes = includeGrantedScopes;
1067                
1068                if (requestObject != null && requestURI != null)
1069                        throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both");
1070                
1071                this.requestObject = requestObject;
1072                this.requestURI = requestURI;
1073                
1074                if (requestObject instanceof SignedJWT) {
1075                        // Make sure the "sub" claim is not set to the client_id value
1076                        // https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-29#section-10.8
1077                        JWTClaimsSet requestObjectClaims;
1078                        try {
1079                                requestObjectClaims = requestObject.getJWTClaimsSet();
1080                        } catch (java.text.ParseException e) {
1081                                // Should never happen
1082                                throw new IllegalArgumentException("Illegal request parameter: " + e.getMessage(), e);
1083                        }
1084                        if (clientID.getValue().equals(requestObjectClaims.getSubject())) {
1085                                throw new IllegalArgumentException("Illegal request parameter: The JWT sub (subject) claim must not equal the client_id");
1086                        }
1087                }
1088                
1089                this.prompt = prompt; // technically OpenID
1090                
1091                this.dpopJKT = dpopJKT;
1092                
1093                this.trustChain = trustChain;
1094
1095                if (MapUtils.isNotEmpty(customParams)) {
1096                        this.customParams = Collections.unmodifiableMap(customParams);
1097                } else {
1098                        this.customParams = Collections.emptyMap();
1099                }
1100        }
1101
1102
1103        /**
1104         * Returns the registered (standard) OAuth 2.0 authorisation request
1105         * parameter names.
1106         *
1107         * @return The registered OAuth 2.0 authorisation request parameter
1108         *         names, as a unmodifiable set.
1109         */
1110        public static Set<String> getRegisteredParameterNames() {
1111
1112                return REGISTERED_PARAMETER_NAMES;
1113        }
1114
1115
1116        /**
1117         * Returns the response type. Corresponds to the {@code response_type}
1118         * parameter.
1119         *
1120         * @return The response type, may be {@code null} for a
1121         *         {@link #specifiesRequestObject() JWT secured authorisation
1122         *         request} with a {@link #getRequestObject() request} or
1123         *         {@link #getRequestURI() request_uri} parameter.
1124         */
1125        public ResponseType getResponseType() {
1126                return rt;
1127        }
1128
1129
1130        /**
1131         * Returns the optional response mode. Corresponds to the optional
1132         * {@code response_mode} parameter.
1133         *
1134         * @return The response mode, {@code null} if not specified.
1135         */
1136        public ResponseMode getResponseMode() {
1137                return rm;
1138        }
1139
1140
1141        /**
1142         * Returns the implied response mode, determined by the optional
1143         * {@code response_mode} parameter, and if that isn't specified, by
1144         * the {@code response_type}.
1145         *
1146         * <p>If the {@link ResponseMode#JWT jwt} response mode shortcut from
1147         * JARM is explicitly requested expands it to
1148         * {@link ResponseMode#QUERY_JWT query.jwt} or
1149         * {@link ResponseMode#FRAGMENT_JWT fragment.jwt} depending on the
1150         * response type ({@code response_type}).
1151         *
1152         * @return The implied response mode.
1153         */
1154        public ResponseMode impliedResponseMode() {
1155                return ResponseMode.resolve(rm, rt);
1156        }
1157
1158
1159        /**
1160         * Returns the client identifier. Corresponds to the {@code client_id}
1161         * parameter.
1162         *
1163         * @return The client identifier.
1164         */
1165        public ClientID getClientID() {
1166                return clientID;
1167        }
1168
1169
1170        /**
1171         * Returns the redirection URI. Corresponds to the optional
1172         * {@code redirection_uri} parameter.
1173         *
1174         * @return The redirection URI, {@code null} if not specified.
1175         */
1176        public URI getRedirectionURI() {
1177                return redirectURI;
1178        }
1179        
1180        
1181        /**
1182         * Returns the scope. Corresponds to the optional {@code scope}
1183         * parameter.
1184         *
1185         * @return The scope, {@code null} if not specified.
1186         */
1187        public Scope getScope() {
1188                return scope;
1189        }
1190        
1191        
1192        /**
1193         * Returns the state. Corresponds to the recommended {@code state}
1194         * parameter.
1195         *
1196         * @return The state, {@code null} if not specified.
1197         */
1198        public State getState() {
1199                return state;
1200        }
1201
1202
1203        /**
1204         * Returns the code challenge for PKCE.
1205         *
1206         * @return The code challenge, {@code null} if not specified.
1207         */
1208        public CodeChallenge getCodeChallenge() {
1209                return codeChallenge;
1210        }
1211
1212
1213        /**
1214         * Returns the code challenge method for PKCE.
1215         *
1216         * @return The code challenge method, {@code null} if not specified.
1217         */
1218        public CodeChallengeMethod getCodeChallengeMethod() {
1219                return codeChallengeMethod;
1220        }
1221        
1222        
1223        /**
1224         * Returns the resource server URI.
1225         *
1226         * @return The resource URI(s), {@code null} if not specified.
1227         */
1228        public List<URI> getResources() {
1229                return resources;
1230        }
1231        
1232        
1233        /**
1234         * Returns {@code true} if incremental authorisation is requested.
1235         *
1236         * @return {@code true} if incremental authorisation is requested,
1237         *         else {@code false}.
1238         */
1239        public boolean includeGrantedScopes() {
1240                return includeGrantedScopes;
1241        }
1242        
1243        
1244        /**
1245         * Returns the request object. Corresponds to the optional
1246         * {@code request} parameter.
1247         *
1248         * @return The request object, {@code null} if not specified.
1249         */
1250        public JWT getRequestObject() {
1251                return requestObject;
1252        }
1253        
1254        
1255        /**
1256         * Returns the request object URI. Corresponds to the optional
1257         * {@code request_uri} parameter.
1258         *
1259         * @return The request object URI, {@code null} if not specified.
1260         */
1261        public URI getRequestURI() {
1262                return requestURI;
1263        }
1264        
1265        
1266        /**
1267         * Returns {@code true} if this is a JWT secured authentication
1268         * request.
1269         *
1270         * @return {@code true} if a request object via a {@code request} or
1271         *         {@code request_uri} parameter is specified, else
1272         *         {@code false}.
1273         */
1274        public boolean specifiesRequestObject() {
1275                return requestObject != null || requestURI != null;
1276        }
1277        
1278        
1279        /**
1280         * Returns the requested prompt. Corresponds to the optional
1281         * {@code prompt} parameter.
1282         *
1283         * @return The requested prompt, {@code null} if not specified.
1284         */
1285        public Prompt getPrompt() {
1286                return prompt;
1287        }
1288        
1289        
1290        /**
1291         * Returns the DPoP JWK SHA-256 thumbprint.
1292         *
1293         * @return The DPoP JWK SHA-256 thumbprint, {@code null} if not
1294         *         specified.
1295         */
1296        public JWKThumbprintConfirmation getDPoPJWKThumbprintConfirmation() {
1297                return dpopJKT;
1298        }
1299        
1300        
1301        /**
1302         * Returns the OpenID Connect Federation 1.0 trust chain.
1303         *
1304         * @return The trust chain, {@code null} if not specified.
1305         */
1306        public TrustChain getTrustChain() {
1307                return trustChain;
1308        }
1309        
1310        
1311        /**
1312         * Returns the additional custom parameters.
1313         *
1314         * @return The additional custom parameters as a unmodifiable map,
1315         *         empty map if none.
1316         */
1317        public Map<String,List<String>> getCustomParameters () {
1318                return customParams;
1319        }
1320        
1321        
1322        /**
1323         * Returns the specified custom parameter.
1324         *
1325         * @param name The parameter name. Must not be {@code null}.
1326         *
1327         * @return The parameter value(s), {@code null} if not specified.
1328         */
1329        public List<String> getCustomParameter(final String name) {
1330                return customParams.get(name);
1331        }
1332        
1333        
1334        /**
1335         * Returns the URI query parameters for this authorisation request.
1336         * Query parameters which are part of the authorisation endpoint are
1337         * not included.
1338         *
1339         * <p>Example parameters:
1340         *
1341         * <pre>
1342         * response_type = code
1343         * client_id     = s6BhdRkqt3
1344         * state         = xyz
1345         * redirect_uri  = https://client.example.com/cb
1346         * </pre>
1347         * 
1348         * @return The parameters.
1349         */
1350        public Map<String,List<String>> toParameters() {
1351                
1352                // Put custom params first, so they may be overwritten by std params
1353                Map<String, List<String>> params = new LinkedHashMap<>(customParams);
1354                
1355                params.put("client_id", Collections.singletonList(getClientID().getValue()));
1356                
1357                if (getResponseType() != null)
1358                        params.put("response_type", Collections.singletonList(getResponseType().toString()));
1359
1360                if (getResponseMode() != null)
1361                        params.put("response_mode", Collections.singletonList(getResponseMode().getValue()));
1362
1363                if (getRedirectionURI() != null)
1364                        params.put("redirect_uri", Collections.singletonList(getRedirectionURI().toString()));
1365
1366                if (getScope() != null)
1367                        params.put("scope", Collections.singletonList(getScope().toString()));
1368                
1369                if (getState() != null)
1370                        params.put("state", Collections.singletonList(getState().getValue()));
1371
1372                if (getCodeChallenge() != null) {
1373                        params.put("code_challenge", Collections.singletonList(getCodeChallenge().getValue()));
1374
1375                        if (getCodeChallengeMethod() != null) {
1376                                params.put("code_challenge_method", Collections.singletonList(getCodeChallengeMethod().getValue()));
1377                        }
1378                }
1379                
1380                if (includeGrantedScopes())
1381                        params.put("include_granted_scopes", Collections.singletonList("true"));
1382                
1383                if (getResources() != null)
1384                        params.put("resource", URIUtils.toStringList(getResources()));
1385                
1386                if (getRequestObject() != null) {
1387                        try {
1388                                params.put("request", Collections.singletonList(getRequestObject().serialize()));
1389                                
1390                        } catch (IllegalStateException e) {
1391                                throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e);
1392                        }
1393                }
1394                
1395                if (getRequestURI() != null)
1396                        params.put("request_uri", Collections.singletonList(getRequestURI().toString()));
1397                
1398                if (getPrompt() != null)
1399                        params.put("prompt", Collections.singletonList(getPrompt().toString()));
1400                
1401                if (getDPoPJWKThumbprintConfirmation() != null)
1402                        params.put("dpop_jkt", Collections.singletonList(getDPoPJWKThumbprintConfirmation().getValue().toString()));
1403                
1404                if (getTrustChain() != null) {
1405                        JSONArray jsonArray = new JSONArray();
1406                        jsonArray.addAll(getTrustChain().toSerializedJWTs());
1407                        params.put("trust_chain", Collections.singletonList(jsonArray.toJSONString()));
1408                }
1409
1410                return params;
1411        }
1412        
1413        
1414        /**
1415         * Returns the parameters for this authorisation request as a JSON Web
1416         * Token (JWT) claims set. Intended for creating a request object.
1417         *
1418         * @return The parameters as JWT claim set.
1419         */
1420        public JWTClaimsSet toJWTClaimsSet() {
1421                
1422                if (specifiesRequestObject()) {
1423                        throw new IllegalStateException("Cannot create nested JWT secured authorization request");
1424                }
1425                
1426                Map<String, List<String>> params = toParameters();
1427                
1428                JWTClaimsSet jwtClaimsSet = JWTClaimsSetUtils.toJWTClaimsSet(params);
1429                
1430                if (params.containsKey("trust_chain")) {
1431                        // JWT claim value must be a JSON array
1432                        jwtClaimsSet = new JWTClaimsSet.Builder(jwtClaimsSet)
1433                                .claim("trust_chain", getTrustChain().toSerializedJWTs())
1434                                .build();
1435                }
1436                
1437                return jwtClaimsSet;
1438        }
1439        
1440        
1441        /**
1442         * Returns the URI query string for this authorisation request.
1443         *
1444         * <p>Note that the '?' character preceding the query string in an URI
1445         * is not included in the returned string.
1446         *
1447         * <p>Example URI query string:
1448         *
1449         * <pre>
1450         * response_type=code
1451         * &amp;client_id=s6BhdRkqt3
1452         * &amp;state=xyz
1453         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1454         * </pre>
1455         * 
1456         * @return The URI query string.
1457         */
1458        public String toQueryString() {
1459                
1460                Map<String, List<String>> params = new HashMap<>();
1461                if (getEndpointURI() != null) {
1462                        params.putAll(URLUtils.parseParameters(getEndpointURI().getQuery()));
1463                }
1464                params.putAll(toParameters());
1465                
1466                return URLUtils.serializeParameters(params);
1467        }
1468
1469
1470        /**
1471         * Returns the complete URI representation for this authorisation
1472         * request, consisting of the {@link #getEndpointURI authorization
1473         * endpoint URI} with the {@link #toQueryString query string} appended.
1474         *
1475         * <p>Example URI:
1476         *
1477         * <pre>
1478         * https://server.example.com/authorize?
1479         * response_type=code
1480         * &amp;client_id=s6BhdRkqt3
1481         * &amp;state=xyz
1482         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1483         * </pre>
1484         *
1485         * @return The URI representation.
1486         */
1487        public URI toURI() {
1488
1489                if (getEndpointURI() == null)
1490                        throw new SerializeException("The authorization endpoint URI is not specified");
1491                
1492                StringBuilder sb = new StringBuilder(URIUtils.stripQueryString(getEndpointURI()).toString());
1493                sb.append('?');
1494                sb.append(toQueryString());
1495                try {
1496                        return new URI(sb.toString());
1497                } catch (URISyntaxException e) {
1498                        throw new SerializeException("Couldn't append query string: " + e.getMessage(), e);
1499                }
1500        }
1501        
1502        
1503        /**
1504         * Returns the matching HTTP request.
1505         *
1506         * @param method The HTTP request method which can be GET or POST. Must
1507         *               not be {@code null}.
1508         *
1509         * @return The HTTP request.
1510         */
1511        public HTTPRequest toHTTPRequest(final HTTPRequest.Method method) {
1512                
1513                if (getEndpointURI() == null)
1514                        throw new SerializeException("The endpoint URI is not specified");
1515                
1516                HTTPRequest httpRequest;
1517                if (method.equals(HTTPRequest.Method.GET)) {
1518                        httpRequest = new HTTPRequest(HTTPRequest.Method.GET, getEndpointURI());
1519                } else if (method.equals(HTTPRequest.Method.POST)) {
1520                        httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI());
1521                } else {
1522                        throw new IllegalArgumentException("The HTTP request method must be GET or POST");
1523                }
1524                
1525                httpRequest.setQuery(toQueryString());
1526                
1527                return httpRequest;
1528        }
1529        
1530        
1531        @Override
1532        public HTTPRequest toHTTPRequest() {
1533        
1534                return toHTTPRequest(HTTPRequest.Method.GET);
1535        }
1536
1537
1538        /**
1539         * Parses an authorisation request from the specified URI query
1540         * parameters.
1541         *
1542         * <p>Example parameters:
1543         *
1544         * <pre>
1545         * response_type = code
1546         * client_id     = s6BhdRkqt3
1547         * state         = xyz
1548         * redirect_uri  = https://client.example.com/cb
1549         * </pre>
1550         *
1551         * @param params The parameters. Must not be {@code null}.
1552         *
1553         * @return The authorisation request.
1554         *
1555         * @throws ParseException If the parameters couldn't be parsed to an
1556         *                        authorisation request.
1557         */
1558        public static AuthorizationRequest parse(final Map<String,List<String>> params)
1559                throws ParseException {
1560
1561                return parse(null, params);
1562        }
1563
1564
1565        /**
1566         * Parses an authorisation request from the specified URI and query
1567         * parameters.
1568         *
1569         * <p>Example parameters:
1570         *
1571         * <pre>
1572         * response_type = code
1573         * client_id     = s6BhdRkqt3
1574         * state         = xyz
1575         * redirect_uri  = https://client.example.com/cb
1576         * </pre>
1577         *
1578         * @param uri    The URI of the authorisation endpoint. May be
1579         *               {@code null} if the {@link #toHTTPRequest()} method
1580         *               will not be used.
1581         * @param params The parameters. Must not be {@code null}.
1582         *
1583         * @return The authorisation request.
1584         *
1585         * @throws ParseException If the parameters couldn't be parsed to an
1586         *                        authorisation request.
1587         */
1588        public static AuthorizationRequest parse(final URI uri, final Map<String,List<String>> params)
1589                throws ParseException {
1590                
1591                Set<String> repeatParams = MultivaluedMapUtils.getKeysWithMoreThanOneValue(params, Collections.singleton("resource"));
1592                
1593                if (! repeatParams.isEmpty()) {
1594                        // Always result in non-redirecting error. Technically
1595                        // only duplicate client_id, state, redirect_uri,
1596                        // response_type, request_uri and request should
1597                        String msg = "Parameter(s) present more than once: " + repeatParams;
1598                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.setDescription(msg));
1599                }
1600                
1601                // Parse response_mode, response_type, client_id, redirect_uri and state first,
1602                // needed if parsing results in a error response
1603                final ClientID clientID;
1604                URI redirectURI = null;
1605                State state = State.parse(MultivaluedMapUtils.getFirstValue(params, "state"));
1606                ResponseMode rm = null;
1607                ResponseType rt = null;
1608                
1609                // Optional response_mode
1610                String v = MultivaluedMapUtils.getFirstValue(params, "response_mode");
1611                if (StringUtils.isNotBlank(v)) {
1612                        rm = new ResponseMode(v);
1613                }
1614                
1615                // Mandatory client_id
1616                v = MultivaluedMapUtils.getFirstValue(params, "client_id");
1617                if (StringUtils.isBlank(v)) {
1618                        // No automatic redirection https://tools.ietf.org/html/rfc6749#section-4.1.2.1
1619                        String msg = "Missing client_id parameter";
1620                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
1621                }
1622                clientID = new ClientID(v);
1623                
1624                // Optional redirect_uri
1625                v = MultivaluedMapUtils.getFirstValue(params, "redirect_uri");
1626                if (StringUtils.isNotBlank(v)) {
1627                        try {
1628                                redirectURI = new URI(v);
1629                        } catch (URISyntaxException e) {
1630                                // No automatic redirection https://tools.ietf.org/html/rfc6749#section-4.1.2.1
1631                                String msg = "Invalid redirect_uri parameter: " + e.getMessage();
1632                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
1633                        }
1634                }
1635                
1636                // Mandatory response_type, unless in JAR
1637                v = MultivaluedMapUtils.getFirstValue(params, "response_type");
1638                if (StringUtils.isNotBlank(v)) {
1639                        try {
1640                                rt = ResponseType.parse(v);
1641                        } catch (ParseException e) {
1642                                // Only cause
1643                                String msg = "Invalid response_type parameter";
1644                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1645                                        clientID, redirectURI, rm, state, e);
1646                        }
1647                }
1648                
1649                // Check for a JAR in request or request_uri parameters
1650                v = MultivaluedMapUtils.getFirstValue(params, "request_uri");
1651                URI requestURI = null;
1652                if (StringUtils.isNotBlank(v)) {
1653                        try {
1654                                requestURI = new URI(v);
1655                        } catch (URISyntaxException e) {
1656                                String msg = "Invalid request_uri parameter: " + e.getMessage();
1657                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1658                                        clientID, redirectURI, ResponseMode.resolve(rm, rt), state, e);
1659                        }
1660                }
1661                
1662                v = MultivaluedMapUtils.getFirstValue(params, "request");
1663                
1664                JWT requestObject = null;
1665                
1666                if (StringUtils.isNotBlank(v)) {
1667                        
1668                        // request_object and request_uri must not be present at the same time
1669                        if (requestURI != null) {
1670                                String msg = "Invalid request: Found mutually exclusive request and request_uri parameters";
1671                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1672                                        clientID, redirectURI, ResponseMode.resolve(rm, rt), state, null);
1673                        }
1674                        
1675                        try {
1676                                requestObject = JWTParser.parse(v);
1677                                
1678                                if (requestObject instanceof SignedJWT) {
1679                                        // Make sure the "sub" claim is not set to the client_id value
1680                                        // https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-29#section-10.8
1681                                        JWTClaimsSet requestObjectClaims = requestObject.getJWTClaimsSet();
1682                                        if (clientID.getValue().equals(requestObjectClaims.getSubject())) {
1683                                                throw new java.text.ParseException("The JWT sub (subject) claim must not equal the client_id", 0);
1684                                        }
1685                                }
1686                                
1687                        } catch (java.text.ParseException e) {
1688                                String msg = "Invalid request parameter: " + e.getMessage();
1689                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1690                                        clientID, redirectURI, ResponseMode.resolve(rm, rt), state, e);
1691                        }
1692                }
1693
1694                // Response type mandatory, unless in JAR
1695                if (rt == null && requestObject == null && requestURI == null) {
1696                        String msg = "Missing response_type parameter";
1697                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1698                                clientID, redirectURI, ResponseMode.resolve(rm, null), state, null);
1699                }
1700
1701
1702                // Parse optional scope
1703                v = MultivaluedMapUtils.getFirstValue(params, "scope");
1704
1705                Scope scope = null;
1706
1707                if (StringUtils.isNotBlank(v))
1708                        scope = Scope.parse(v);
1709
1710
1711                // Parse optional code challenge and method for PKCE
1712                CodeChallenge codeChallenge = null;
1713                CodeChallengeMethod codeChallengeMethod = null;
1714
1715                v = MultivaluedMapUtils.getFirstValue(params, "code_challenge");
1716
1717                if (StringUtils.isNotBlank(v))
1718                        codeChallenge = CodeChallenge.parse(v);
1719
1720                if (codeChallenge != null) {
1721
1722                        v = MultivaluedMapUtils.getFirstValue(params, "code_challenge_method");
1723
1724                        if (StringUtils.isNotBlank(v))
1725                                codeChallengeMethod = CodeChallengeMethod.parse(v);
1726                }
1727                
1728                
1729                List<URI> resources;
1730                try {
1731                        resources = ResourceUtils.parseResourceURIs(params.get("resource"));
1732                } catch (ParseException e) {
1733                        throw new ParseException(e.getMessage(), OAuth2Error.INVALID_TARGET.setDescription(e.getMessage()),
1734                                clientID, redirectURI, ResponseMode.resolve(rm, rt), state, e);
1735                }
1736                
1737                boolean includeGrantedScopes = false;
1738                v = MultivaluedMapUtils.getFirstValue(params, "include_granted_scopes");
1739                if ("true".equals(v)) {
1740                        includeGrantedScopes = true;
1741                }
1742                
1743                Prompt prompt;
1744                try {
1745                        prompt = Prompt.parse(MultivaluedMapUtils.getFirstValue(params, "prompt"));
1746                        
1747                } catch (ParseException e) {
1748                        String msg = "Invalid prompt parameter: " + e.getMessage();
1749                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
1750                                clientID, redirectURI, ResponseMode.resolve(rm, rt), state, e);
1751                }
1752                
1753                JWKThumbprintConfirmation dpopJKT = null;
1754                v = MultivaluedMapUtils.getFirstValue(params, "dpop_jkt");
1755                if (StringUtils.isNotBlank(v)) {
1756                        dpopJKT = new JWKThumbprintConfirmation(new Base64URL(v));
1757                }
1758                
1759                TrustChain trustChain = null;
1760                v = MultivaluedMapUtils.getFirstValue(params, "trust_chain");
1761                if (StringUtils.isNotBlank(v)) {
1762                        JSONArray jsonArray = JSONArrayUtils.parse(v);
1763                        trustChain = TrustChain.parseSerialized(JSONArrayUtils.toStringList(jsonArray));
1764                }
1765                
1766                // Parse custom parameters
1767                Map<String,List<String>> customParams = null;
1768
1769                for (Map.Entry<String,List<String>> p: params.entrySet()) {
1770
1771                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1772                                // We have a custom parameter
1773                                if (customParams == null) {
1774                                        customParams = new HashMap<>();
1775                                }
1776                                customParams.put(p.getKey(), p.getValue());
1777                        }
1778                }
1779
1780
1781                return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state,
1782                        codeChallenge, codeChallengeMethod, resources, includeGrantedScopes,
1783                        requestObject, requestURI,
1784                        prompt, dpopJKT, trustChain,
1785                        customParams);
1786        }
1787
1788
1789        /**
1790         * Parses an authorisation request from the specified URI query string.
1791         *
1792         * <p>Example URI query string:
1793         *
1794         * <pre>
1795         * response_type=code
1796         * &amp;client_id=s6BhdRkqt3
1797         * &amp;state=xyz
1798         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1799         * </pre>
1800         *
1801         * @param query The URI query string. Must not be {@code null}.
1802         *
1803         * @return The authorisation request.
1804         *
1805         * @throws ParseException If the query string couldn't be parsed to an
1806         *                        authorisation request.
1807         */
1808        public static AuthorizationRequest parse(final String query)
1809                throws ParseException {
1810
1811                return parse(null, URLUtils.parseParameters(query));
1812        }
1813        
1814        
1815        /**
1816         * Parses an authorisation request from the specified URI and query
1817         * string.
1818         *
1819         * <p>Example URI query string:
1820         *
1821         * <pre>
1822         * response_type=code
1823         * &amp;client_id=s6BhdRkqt3
1824         * &amp;state=xyz
1825         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1826         * </pre>
1827         *
1828         * @param uri   The URI of the authorisation endpoint. May be 
1829         *              {@code null} if the {@link #toHTTPRequest()} method
1830         *              will not be used.
1831         * @param query The URI query string. Must not be {@code null}.
1832         *
1833         * @return The authorisation request.
1834         *
1835         * @throws ParseException If the query string couldn't be parsed to an 
1836         *                        authorisation request.
1837         */
1838        public static AuthorizationRequest parse(final URI uri, final String query)
1839                throws ParseException {
1840        
1841                return parse(uri, URLUtils.parseParameters(query));
1842        }
1843
1844
1845        /**
1846         * Parses an authorisation request from the specified URI.
1847         *
1848         * <p>Example URI:
1849         *
1850         * <pre>
1851         * https://server.example.com/authorize?
1852         * response_type=code
1853         * &amp;client_id=s6BhdRkqt3
1854         * &amp;state=xyz
1855         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1856         * </pre>
1857         *
1858         * @param uri The URI. Must not be {@code null}.
1859         *
1860         * @return The authorisation request.
1861         *
1862         * @throws ParseException If the URI couldn't be parsed to an
1863         *                        authorisation request.
1864         */
1865        public static AuthorizationRequest parse(final URI uri)
1866                throws ParseException {
1867
1868                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1869        }
1870        
1871        
1872        /**
1873         * Parses an authorisation request from the specified HTTP request.
1874         *
1875         * <p>Example HTTP request (GET):
1876         *
1877         * <pre>
1878         * https://server.example.com/authorize?
1879         * response_type=code
1880         * &amp;client_id=s6BhdRkqt3
1881         * &amp;state=xyz
1882         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1883         * </pre>
1884         *
1885         * @param httpRequest The HTTP request. Must not be {@code null}.
1886         *
1887         * @return The authorisation request.
1888         *
1889         * @throws ParseException If the HTTP request couldn't be parsed to an 
1890         *                        authorisation request.
1891         */
1892        public static AuthorizationRequest parse(final HTTPRequest httpRequest) 
1893                throws ParseException {
1894                
1895                String query = httpRequest.getQuery();
1896                
1897                if (query == null)
1898                        throw new ParseException("Missing URI query string");
1899                
1900                return parse(URIUtils.getBaseURI(httpRequest.getURI()), query);
1901        }
1902}