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