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