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