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