001package com.nimbusds.oauth2.sdk;
002
003
004import java.net.MalformedURLException;
005import java.net.URI;
006import java.net.URISyntaxException;
007import java.net.URL;
008import java.util.LinkedHashMap;
009import java.util.Map;
010
011import com.nimbusds.oauth2.sdk.http.HTTPRequest;
012import com.nimbusds.oauth2.sdk.id.ClientID;
013import com.nimbusds.oauth2.sdk.id.State;
014import com.nimbusds.oauth2.sdk.pkce.CodeChallenge;
015import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod;
016import com.nimbusds.oauth2.sdk.util.URIUtils;
017import com.nimbusds.oauth2.sdk.util.URLUtils;
018import net.jcip.annotations.Immutable;
019import org.apache.commons.lang3.StringUtils;
020
021
022/**
023 * Authorisation request. Used to authenticate an end-user and request the
024 * end-user's consent to grant the client access to a protected resource.
025 *
026 * <p>Extending classes may define additional request parameters as well as 
027 * enforce tighter requirements on the base parameters.
028 *
029 * <p>Example HTTP request:
030 *
031 * <pre>
032 * https://server.example.com/authorize?
033 * response_type=code
034 * &amp;client_id=s6BhdRkqt3
035 * &amp;state=xyz
036 * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
037 * </pre>
038 *
039 * <p>Related specifications:
040 *
041 * <ul>
042 *     <li>OAuth 2.0 (RFC 6749), sections 4.1.1 and 4.2.1.
043 *     <li>OAuth 2.0 Multiple Response Type Encoding Practices 1.0.
044 *     <li>OAuth 2.0 Form Post Response Mode 1.0.
045 *     <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636).
046 * </ul>
047 */
048@Immutable
049public class AuthorizationRequest extends AbstractRequest {
050
051
052        /**
053         * The response type (required).
054         */
055        private final ResponseType rt;
056
057
058        /**
059         * The client identifier (required).
060         */
061        private final ClientID clientID;
062
063
064        /**
065         * The redirection URI where the response will be sent (optional). 
066         */
067        private final URI redirectURI;
068        
069        
070        /**
071         * The scope (optional).
072         */
073        private final Scope scope;
074        
075        
076        /**
077         * The opaque value to maintain state between the request and the 
078         * callback (recommended).
079         */
080        private final State state;
081
082
083        /**
084         * The response mode (optional).
085         */
086        private final ResponseMode rm;
087
088
089        /**
090         * The authorisation code challenge for PKCE (optional).
091         */
092        private final CodeChallenge codeChallenge;
093
094
095        /**
096         * The authorisation code challenge method for PKCE (optional).
097         */
098        private final CodeChallengeMethod codeChallengeMethod;
099
100
101        /**
102         * Builder for constructing authorisation requests.
103         */
104        public static class Builder {
105
106
107                /**
108                 * The endpoint URI (optional).
109                 */
110                private URI uri;
111
112
113                /**
114                 * The response type (required).
115                 */
116                private final ResponseType rt;
117
118
119                /**
120                 * The client identifier (required).
121                 */
122                private final ClientID clientID;
123
124
125                /**
126                 * The redirection URI where the response will be sent
127                 * (optional).
128                 */
129                private URI redirectURI;
130
131
132                /**
133                 * The scope (optional).
134                 */
135                private Scope scope;
136
137
138                /**
139                 * The opaque value to maintain state between the request and
140                 * the callback (recommended).
141                 */
142                private State state;
143
144
145                /**
146                 * The response mode (optional).
147                 */
148                private ResponseMode rm;
149
150
151                /**
152                 * The authorisation code challenge for PKCE (optional).
153                 */
154                private CodeChallenge codeChallenge;
155
156
157                /**
158                 * The authorisation code challenge method for PKCE (optional).
159                 */
160                private CodeChallengeMethod codeChallengeMethod;
161
162
163                /**
164                 * Creates a new authorisation request builder.
165                 *
166                 * @param rt       The response type. Corresponds to the
167                 *                 {@code response_type} parameter. Must not be
168                 *                 {@code null}.
169                 * @param clientID The client identifier. Corresponds to the
170                 *                 {@code client_id} parameter. Must not be
171                 *                 {@code null}.
172                 */
173                public Builder(final ResponseType rt, final ClientID clientID) {
174
175                        if (rt == null)
176                                throw new IllegalArgumentException("The response type must not be null");
177
178                        this.rt = rt;
179
180
181                        if (clientID == null)
182                                throw new IllegalArgumentException("The client ID must not be null");
183
184                        this.clientID = clientID;
185                }
186
187
188                /**
189                 * Sets the redirection URI. Corresponds to the optional
190                 * {@code redirection_uri} parameter.
191                 *
192                 * @param redirectURI The redirection URI, {@code null} if not
193                 *                    specified.
194                 *
195                 * @return This builder.
196                 */
197                public Builder redirectionURI(final URI redirectURI) {
198
199                        this.redirectURI = redirectURI;
200                        return this;
201                }
202
203
204                /**
205                 * Sets the scope. Corresponds to the optional {@code scope}
206                 * parameter.
207                 *
208                 * @param scope The scope, {@code null} if not specified.
209                 *
210                 * @return This builder.
211                 */
212                public Builder scope(final Scope scope) {
213
214                        this.scope = scope;
215                        return this;
216                }
217
218
219                /**
220                 * Sets the state. Corresponds to the recommended {@code state}
221                 * parameter.
222                 *
223                 * @param state The state, {@code null} if not specified.
224                 *
225                 * @return This builder.
226                 */
227                public Builder state(final State state) {
228
229                        this.state = state;
230                        return this;
231                }
232
233
234                /**
235                 * Sets the response mode. Corresponds to the optional
236                 * {@code response_mode} parameter. Use of this parameter is
237                 * not recommended unless a non-default response mode is
238                 * requested (e.g. form_post).
239                 *
240                 * @param rm The response mode, {@code null} if not specified.
241                 *
242                 * @return This builder.
243                 */
244                public Builder responseMode(final ResponseMode rm) {
245
246                        this.rm = rm;
247                        return this;
248                }
249
250
251                /**
252                 * Sets the code challenge for Proof Key for Code Exchange
253                 * (PKCE) by public OAuth clients.
254                 *
255                 * @param codeChallenge       The code challenge, {@code null}
256                 *                            if not specified.
257                 * @param codeChallengeMethod The code challenge method,
258                 *                            {@code null} if not specified.
259                 *
260                 * @return This builder.
261                 */
262                public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) {
263
264                        this.codeChallenge = codeChallenge;
265                        this.codeChallengeMethod = codeChallengeMethod;
266                        return this;
267                }
268
269
270                /**
271                 * Sets the URI of the endpoint (HTTP or HTTPS) for which the
272                 * request is intended.
273                 *
274                 * @param uri The endpoint URI, {@code null} if not specified.
275                 *
276                 * @return This builder.
277                 */
278                public Builder endpointURI(final URI uri) {
279
280                        this.uri = uri;
281                        return this;
282                }
283
284
285                /**
286                 * Builds a new authorisation request.
287                 *
288                 * @return The authorisation request.
289                 */
290                public AuthorizationRequest build() {
291
292                        return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod);
293                }
294        }
295
296
297        /**
298         * Creates a new minimal authorisation request.
299         *
300         * @param uri      The URI of the authorisation endpoint. May be
301         *                 {@code null} if the {@link #toHTTPRequest} method
302         *                 will not be used.
303         * @param rt       The response type. Corresponds to the
304         *                 {@code response_type} parameter. Must not be
305         *                 {@code null}.
306         * @param clientID The client identifier. Corresponds to the
307         *                 {@code client_id} parameter. Must not be
308         *                 {@code null}.
309         */
310        public AuthorizationRequest(final URI uri,
311                                    final ResponseType rt,
312                                    final ClientID clientID) {
313
314                this(uri, rt, null, clientID, null, null, null, null, null);
315        }
316
317
318        /**
319         * Creates a new authorisation request.
320         *
321         * @param uri                 The URI of the authorisation endpoint.
322         *                            May be {@code null} if the
323         *                            {@link #toHTTPRequest} method will not be
324         *                            used.
325         * @param rt                  The response type. Corresponds to the
326         *                            {@code response_type} parameter. Must not
327         *                            be {@code null}.
328         * @param rm                  The response mode. Corresponds to the
329         *                            optional {@code response_mode} parameter.
330         *                            Use of this parameter is not recommended
331         *                            unless a non-default response mode is
332         *                            requested (e.g. form_post).
333         * @param clientID            The client identifier. Corresponds to the
334         *                            {@code client_id} parameter. Must not be
335         *                            {@code null}.
336         * @param redirectURI         The redirection URI. Corresponds to the
337         *                            optional {@code redirect_uri} parameter.
338         *                            {@code null} if not specified.
339         * @param scope               The request scope. Corresponds to the
340         *                            optional {@code scope} parameter.
341         *                            {@code null} if not specified.
342         * @param state               The state. Corresponds to the recommended
343         *                            {@code state} parameter. {@code null} if
344         *                            not specified.
345         */
346        public AuthorizationRequest(final URI uri,
347                                    final ResponseType rt,
348                                    final ResponseMode rm,
349                                    final ClientID clientID,
350                                    final URI redirectURI,
351                                    final Scope scope,
352                                    final State state) {
353
354                this(uri, rt, rm, clientID, redirectURI, scope, state, null, null);
355        }
356
357
358        /**
359         * Creates a new authorisation request with PKCE support.
360         *
361         * @param uri                 The URI of the authorisation endpoint.
362         *                            May be {@code null} if the
363         *                            {@link #toHTTPRequest} method will not be
364         *                            used.
365         * @param rt                  The response type. Corresponds to the
366         *                            {@code response_type} parameter. Must not
367         *                            be {@code null}.
368         * @param rm                  The response mode. Corresponds to the
369         *                            optional {@code response_mode} parameter.
370         *                            Use of this parameter is not recommended
371         *                            unless a non-default response mode is
372         *                            requested (e.g. form_post).
373         * @param clientID            The client identifier. Corresponds to the
374         *                            {@code client_id} parameter. Must not be
375         *                            {@code null}.
376         * @param redirectURI         The redirection URI. Corresponds to the
377         *                            optional {@code redirect_uri} parameter.
378         *                            {@code null} if not specified.
379         * @param scope               The request scope. Corresponds to the
380         *                            optional {@code scope} parameter.
381         *                            {@code null} if not specified.
382         * @param state               The state. Corresponds to the recommended
383         *                            {@code state} parameter. {@code null} if
384         *                            not specified.
385         * @param codeChallenge       The code challenge for PKCE, {@code null}
386         *                            if not specified.
387         * @param codeChallengeMethod The code challenge method for PKCE,
388         *                            {@code null} if not specified.
389         */
390        public AuthorizationRequest(final URI uri,
391                                    final ResponseType rt,
392                                    final ResponseMode rm,
393                                    final ClientID clientID,
394                                    final URI redirectURI,
395                                    final Scope scope,
396                                    final State state,
397                                    final CodeChallenge codeChallenge,
398                                    final CodeChallengeMethod codeChallengeMethod) {
399
400                super(uri);
401
402                if (rt == null)
403                        throw new IllegalArgumentException("The response type must not be null");
404
405                this.rt = rt;
406
407                this.rm = rm;
408
409
410                if (clientID == null)
411                        throw new IllegalArgumentException("The client ID must not be null");
412
413                this.clientID = clientID;
414
415
416                this.redirectURI = redirectURI;
417                this.scope = scope;
418                this.state = state;
419
420                this.codeChallenge = codeChallenge;
421                this.codeChallengeMethod = codeChallengeMethod;
422        }
423
424
425        /**
426         * Gets the response type. Corresponds to the {@code response_type}
427         * parameter.
428         *
429         * @return The response type.
430         */
431        public ResponseType getResponseType() {
432        
433                return rt;
434        }
435
436
437        /**
438         * Gets the optional response mode. Corresponds to the optional
439         * {@code response_mode} parameter.
440         *
441         * @return The response mode, {@code null} if not specified.
442         */
443        public ResponseMode getResponseMode() {
444
445                return rm;
446        }
447
448
449        /**
450         * Returns the implied response mode, determined by the optional
451         * {@code response_mode} parameter, and if that isn't specified, by
452         * the {@code response_type}.
453         *
454         * @return The implied response mode.
455         */
456        public ResponseMode impliedResponseMode() {
457
458                if (rm != null) {
459                        return rm;
460                } else if (rt.impliesImplicitFlow()) {
461                        return ResponseMode.FRAGMENT;
462                } else {
463                        return ResponseMode.QUERY;
464                }
465        }
466
467
468        /**
469         * Gets the client identifier. Corresponds to the {@code client_id} 
470         * parameter.
471         *
472         * @return The client identifier.
473         */
474        public ClientID getClientID() {
475        
476                return clientID;
477        }
478
479
480        /**
481         * Gets the redirection URI. Corresponds to the optional 
482         * {@code redirection_uri} parameter.
483         *
484         * @return The redirection URI, {@code null} if not specified.
485         */
486        public URI getRedirectionURI() {
487        
488                return redirectURI;
489        }
490        
491        
492        /**
493         * Gets the scope. Corresponds to the optional {@code scope} parameter.
494         *
495         * @return The scope, {@code null} if not specified.
496         */
497        public Scope getScope() {
498        
499                return scope;
500        }
501        
502        
503        /**
504         * Gets the state. Corresponds to the recommended {@code state} 
505         * parameter.
506         *
507         * @return The state, {@code null} if not specified.
508         */
509        public State getState() {
510        
511                return state;
512        }
513
514
515        /**
516         * Returns the code challenge for PKCE.
517         *
518         * @return The code challenge, {@code null} if not specified.
519         */
520        public CodeChallenge getCodeChallenge() {
521
522                return codeChallenge;
523        }
524
525
526        /**
527         * Returns the code challenge method for PKCE.
528         *
529         * @return The code challenge method, {@code null} if not specified.
530         */
531        public CodeChallengeMethod getCodeChallengeMethod() {
532
533                return codeChallengeMethod;
534        }
535
536
537        /**
538         * Returns the parameters for this authorisation request.
539         *
540         * <p>Example parameters:
541         *
542         * <pre>
543         * response_type = code
544         * client_id     = s6BhdRkqt3
545         * state         = xyz
546         * redirect_uri  = https://client.example.com/cb
547         * </pre>
548         * 
549         * @return The parameters.
550         */
551        public Map<String,String> toParameters() {
552
553                Map <String,String> params = new LinkedHashMap<>();
554                
555                params.put("response_type", rt.toString());
556                params.put("client_id", clientID.getValue());
557
558                if (rm != null) {
559                        params.put("response_mode", rm.getValue());
560                }
561
562                if (redirectURI != null)
563                        params.put("redirect_uri", redirectURI.toString());
564
565                if (scope != null)
566                        params.put("scope", scope.toString());
567                
568                if (state != null)
569                        params.put("state", state.getValue());
570
571                if (codeChallenge != null) {
572                        params.put("code_challenge", codeChallenge.getValue());
573
574                        if (codeChallengeMethod != null) {
575                                params.put("code_challenge_method", codeChallengeMethod.getValue());
576                        }
577                }
578
579                return params;
580        }
581        
582        
583        /**
584         * Returns the URI query string for this authorisation request.
585         *
586         * <p>Note that the '?' character preceding the query string in an URI
587         * is not included in the returned string.
588         *
589         * <p>Example URI query string:
590         *
591         * <pre>
592         * response_type=code
593         * &amp;client_id=s6BhdRkqt3
594         * &amp;state=xyz
595         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
596         * </pre>
597         * 
598         * @return The URI query string.
599         */
600        public String toQueryString() {
601                
602                return URLUtils.serializeParameters(toParameters());
603        }
604
605
606        /**
607         * Returns the complete URI representation for this authorisation
608         * request, consisting of the {@link #getEndpointURI authorization
609         * endpoint URI} with the {@link #toQueryString query string} appended.
610         *
611         * <p>Example URI:
612         *
613         * <pre>
614         * https://server.example.com/authorize?
615         * response_type=code
616         * &amp;client_id=s6BhdRkqt3
617         * &amp;state=xyz
618         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
619         * </pre>
620         *
621         * @return The URI representation.
622         */
623        public URI toURI() {
624
625                if (getEndpointURI() == null)
626                        throw new SerializeException("The authorization endpoint URI is not specified");
627
628                StringBuilder sb = new StringBuilder(getEndpointURI().toString());
629                sb.append('?');
630                sb.append(toQueryString());
631                try {
632                        return new URI(sb.toString());
633                } catch (URISyntaxException e) {
634                        throw new SerializeException("Couldn't append query string: " + e.getMessage(), e);
635                }
636        }
637        
638        
639        /**
640         * Returns the matching HTTP request.
641         *
642         * @param method The HTTP request method which can be GET or POST. Must
643         *               not be {@code null}.
644         *
645         * @return The HTTP request.
646         */
647        public HTTPRequest toHTTPRequest(final HTTPRequest.Method method) {
648                
649                if (getEndpointURI() == null)
650                        throw new SerializeException("The endpoint URI is not specified");
651                
652                HTTPRequest httpRequest;
653
654                URL endpointURL;
655
656                try {
657                        endpointURL = getEndpointURI().toURL();
658
659                } catch (MalformedURLException e) {
660
661                        throw new SerializeException(e.getMessage(), e);
662                }
663                
664                if (method.equals(HTTPRequest.Method.GET)) {
665
666                        httpRequest = new HTTPRequest(HTTPRequest.Method.GET, endpointURL);
667
668                } else if (method.equals(HTTPRequest.Method.POST)) {
669
670                        httpRequest = new HTTPRequest(HTTPRequest.Method.POST, endpointURL);
671
672                } else {
673
674                        throw new IllegalArgumentException("The HTTP request method must be GET or POST");
675                }
676                
677                httpRequest.setQuery(toQueryString());
678                
679                return httpRequest;
680        }
681        
682        
683        @Override
684        public HTTPRequest toHTTPRequest() {
685        
686                return toHTTPRequest(HTTPRequest.Method.GET);
687        }
688
689
690        /**
691         * Parses an authorisation request from the specified parameters.
692         *
693         * <p>Example parameters:
694         *
695         * <pre>
696         * response_type = code
697         * client_id     = s6BhdRkqt3
698         * state         = xyz
699         * redirect_uri  = https://client.example.com/cb
700         * </pre>
701         *
702         * @param params The parameters. Must not be {@code null}.
703         *
704         * @return The authorisation request.
705         *
706         * @throws ParseException If the parameters couldn't be parsed to an
707         *                        authorisation request.
708         */
709        public static AuthorizationRequest parse(final Map<String,String> params)
710                throws ParseException {
711
712                return parse(null, params);
713        }
714
715
716        /**
717         * Parses an authorisation request from the specified parameters.
718         *
719         * <p>Example parameters:
720         *
721         * <pre>
722         * response_type = code
723         * client_id     = s6BhdRkqt3
724         * state         = xyz
725         * redirect_uri  = https://client.example.com/cb
726         * </pre>
727         *
728         * @param uri    The URI of the authorisation endpoint. May be
729         *               {@code null} if the {@link #toHTTPRequest()} method
730         *               will not be used.
731         * @param params The parameters. Must not be {@code null}.
732         *
733         * @return The authorisation request.
734         *
735         * @throws ParseException If the parameters couldn't be parsed to an
736         *                        authorisation request.
737         */
738        public static AuthorizationRequest parse(final URI uri, final Map<String,String> params)
739                throws ParseException {
740
741                // Parse mandatory client ID first
742                String v = params.get("client_id");
743
744                if (StringUtils.isBlank(v)) {
745                        String msg = "Missing \"client_id\" parameter";
746                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
747                }
748
749                ClientID clientID = new ClientID(v);
750
751
752                // Parse optional redirection URI second
753                v = params.get("redirect_uri");
754
755                URI redirectURI = null;
756
757                if (StringUtils.isNotBlank(v)) {
758
759                        try {
760                                redirectURI = new URI(v);
761
762                        } catch (URISyntaxException e) {
763                                String msg = "Invalid \"redirect_uri\" parameter: " + e.getMessage();
764                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
765                                                         clientID, null, null, null, e);
766                        }
767                }
768
769
770                // Parse optional state third
771                State state = State.parse(params.get("state"));
772
773
774                // Parse mandatory response type
775                v = params.get("response_type");
776
777                ResponseType rt;
778
779                try {
780                        rt = ResponseType.parse(v);
781
782                } catch (ParseException e) {
783                        // Only cause
784                        String msg = "Missing \"response_type\" parameter";
785                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
786                                                 clientID, redirectURI, null, state, e);
787                }
788
789
790                // Parse the optional response mode
791                v = params.get("response_mode");
792
793                ResponseMode rm = null;
794
795                if (StringUtils.isNotBlank(v)) {
796                        rm = new ResponseMode(v);
797                }
798
799
800                // Parse optional scope
801                v = params.get("scope");
802
803                Scope scope = null;
804
805                if (StringUtils.isNotBlank(v))
806                        scope = Scope.parse(v);
807
808
809                // Parse optional code challenge and method for PKCE
810                CodeChallenge codeChallenge = null;
811                CodeChallengeMethod codeChallengeMethod = null;
812
813                v = params.get("code_challenge");
814
815                if (StringUtils.isNotBlank(v))
816                        codeChallenge = new CodeChallenge(v);
817
818                if (codeChallenge != null) {
819
820                        v = params.get("code_challenge_method");
821
822                        if (StringUtils.isNotBlank(v))
823                                codeChallengeMethod = CodeChallengeMethod.parse(v);
824                }
825
826
827                return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod);
828        }
829
830
831        /**
832         * Parses an authorisation request from the specified URI query string.
833         *
834         * <p>Example URI query string:
835         *
836         * <pre>
837         * response_type=code
838         * &amp;client_id=s6BhdRkqt3
839         * &amp;state=xyz
840         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
841         * </pre>
842         *
843         * @param query The URI query string. Must not be {@code null}.
844         *
845         * @return The authorisation request.
846         *
847         * @throws ParseException If the query string couldn't be parsed to an
848         *                        authorisation request.
849         */
850        public static AuthorizationRequest parse(final String query)
851                throws ParseException {
852
853                return parse(null, URLUtils.parseParameters(query));
854        }
855        
856        
857        /**
858         * Parses an authorisation request from the specified URI query string.
859         *
860         * <p>Example URI query string:
861         *
862         * <pre>
863         * response_type=code
864         * &amp;client_id=s6BhdRkqt3
865         * &amp;state=xyz
866         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
867         * </pre>
868         *
869         * @param uri   The URI of the authorisation endpoint. May be 
870         *              {@code null} if the {@link #toHTTPRequest()} method
871         *              will not be used.
872         * @param query The URI query string. Must not be {@code null}.
873         *
874         * @return The authorisation request.
875         *
876         * @throws ParseException If the query string couldn't be parsed to an 
877         *                        authorisation request.
878         */
879        public static AuthorizationRequest parse(final URI uri, final String query)
880                throws ParseException {
881        
882                return parse(uri, URLUtils.parseParameters(query));
883        }
884
885
886        /**
887         * Parses an authorisation request from the specified URI.
888         *
889         * <p>Example URI:
890         *
891         * <pre>
892         * https://server.example.com/authorize?
893         * response_type=code
894         * &amp;client_id=s6BhdRkqt3
895         * &amp;state=xyz
896         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
897         * </pre>
898         *
899         * @param uri The URI. Must not be {@code null}.
900         *
901         * @return The authorisation request.
902         *
903         * @throws ParseException If the URI couldn't be parsed to an
904         *                        authorisation request.
905         */
906        public static AuthorizationRequest parse(final URI uri)
907                throws ParseException {
908
909                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
910        }
911        
912        
913        /**
914         * Parses an authorisation request from the specified HTTP request.
915         *
916         * <p>Example HTTP request (GET):
917         *
918         * <pre>
919         * https://server.example.com/authorize?
920         * response_type=code
921         * &amp;client_id=s6BhdRkqt3
922         * &amp;state=xyz
923         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
924         * </pre>
925         *
926         * @param httpRequest The HTTP request. Must not be {@code null}.
927         *
928         * @return The authorisation request.
929         *
930         * @throws ParseException If the HTTP request couldn't be parsed to an 
931         *                        authorisation request.
932         */
933        public static AuthorizationRequest parse(final HTTPRequest httpRequest) 
934                throws ParseException {
935                
936                String query = httpRequest.getQuery();
937                
938                if (query == null)
939                        throw new ParseException("Missing URI query string");
940
941                try {
942                        return parse(URIUtils.getBaseURI(httpRequest.getURL().toURI()), query);
943
944                } catch (URISyntaxException e) {
945
946                        throw new ParseException(e.getMessage(), e);
947                }
948        }
949}