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