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.URIUtils;
034import com.nimbusds.oauth2.sdk.util.URLUtils;
035import net.jcip.annotations.Immutable;
036import org.apache.commons.collections4.MapUtils;
037import org.apache.commons.lang3.StringUtils;
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                 * Sets the redirection URI. Corresponds to the optional
246                 * {@code redirection_uri} parameter.
247                 *
248                 * @param redirectURI The redirection URI, {@code null} if not
249                 *                    specified.
250                 *
251                 * @return This builder.
252                 */
253                public Builder redirectionURI(final URI redirectURI) {
254
255                        this.redirectURI = redirectURI;
256                        return this;
257                }
258
259
260                /**
261                 * Sets the scope. Corresponds to the optional {@code scope}
262                 * parameter.
263                 *
264                 * @param scope The scope, {@code null} if not specified.
265                 *
266                 * @return This builder.
267                 */
268                public Builder scope(final Scope scope) {
269
270                        this.scope = scope;
271                        return this;
272                }
273
274
275                /**
276                 * Sets the state. Corresponds to the recommended {@code state}
277                 * parameter.
278                 *
279                 * @param state The state, {@code null} if not specified.
280                 *
281                 * @return This builder.
282                 */
283                public Builder state(final State state) {
284
285                        this.state = state;
286                        return this;
287                }
288
289
290                /**
291                 * Sets the response mode. Corresponds to the optional
292                 * {@code response_mode} parameter. Use of this parameter is
293                 * not recommended unless a non-default response mode is
294                 * requested (e.g. form_post).
295                 *
296                 * @param rm The response mode, {@code null} if not specified.
297                 *
298                 * @return This builder.
299                 */
300                public Builder responseMode(final ResponseMode rm) {
301
302                        this.rm = rm;
303                        return this;
304                }
305
306
307                /**
308                 * Sets the code challenge for Proof Key for Code Exchange
309                 * (PKCE) by public OAuth clients.
310                 *
311                 * @param codeChallenge       The code challenge, {@code null}
312                 *                            if not specified.
313                 * @param codeChallengeMethod The code challenge method,
314                 *                            {@code null} if not specified.
315                 *
316                 * @return This builder.
317                 */
318                @Deprecated
319                public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) {
320
321                        this.codeChallenge = codeChallenge;
322                        this.codeChallengeMethod = codeChallengeMethod;
323                        return this;
324                }
325
326
327                /**
328                 * Sets the code challenge for Proof Key for Code Exchange
329                 * (PKCE) by public OAuth clients.
330                 *
331                 * @param codeVerifier        The code verifier to use to
332                 *                            compute the code challenge,
333                 *                            {@code null} if PKCE is not
334                 *                            specified.
335                 * @param codeChallengeMethod The code challenge method,
336                 *                            {@code null} if not specified.
337                 *                            Defaults to
338                 *                            {@link CodeChallengeMethod#PLAIN}
339                 *                            if a code verifier is specified.
340                 *
341                 * @return This builder.
342                 */
343                public Builder codeChallenge(final CodeVerifier codeVerifier, final CodeChallengeMethod codeChallengeMethod) {
344
345                        if (codeVerifier != null) {
346                                CodeChallengeMethod method = codeChallengeMethod != null ? codeChallengeMethod : CodeChallengeMethod.getDefault();
347                                this.codeChallenge = CodeChallenge.compute(method, codeVerifier);
348                                this.codeChallengeMethod = method;
349                        } else {
350                                this.codeChallenge = null;
351                                this.codeChallengeMethod = null;
352                        }
353                        return this;
354                }
355
356
357                /**
358                 * Sets the specified additional custom parameter.
359                 *
360                 * @param name  The parameter name. Must not be {@code null}.
361                 * @param value The parameter value, {@code null} if not
362                 *              specified.
363                 *
364                 * @return This builder.
365                 */
366                public Builder customParameter(final String name, final String value) {
367
368                        customParams.put(name, value);
369                        return this;
370                }
371
372
373                /**
374                 * Sets the URI of the endpoint (HTTP or HTTPS) for which the
375                 * request is intended.
376                 *
377                 * @param uri The endpoint URI, {@code null} if not specified.
378                 *
379                 * @return This builder.
380                 */
381                public Builder endpointURI(final URI uri) {
382
383                        this.uri = uri;
384                        return this;
385                }
386
387
388                /**
389                 * Builds a new authorisation request.
390                 *
391                 * @return The authorisation request.
392                 */
393                public AuthorizationRequest build() {
394
395                        return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams);
396                }
397        }
398
399
400        /**
401         * Creates a new minimal authorisation request.
402         *
403         * @param uri      The URI of the authorisation endpoint. May be
404         *                 {@code null} if the {@link #toHTTPRequest} method
405         *                 will not be used.
406         * @param rt       The response type. Corresponds to the
407         *                 {@code response_type} parameter. Must not be
408         *                 {@code null}.
409         * @param clientID The client identifier. Corresponds to the
410         *                 {@code client_id} parameter. Must not be
411         *                 {@code null}.
412         */
413        public AuthorizationRequest(final URI uri,
414                                    final ResponseType rt,
415                                    final ClientID clientID) {
416
417                this(uri, rt, null, clientID, null, null, null, null, null);
418        }
419
420
421        /**
422         * Creates a new authorisation request.
423         *
424         * @param uri                 The URI of the authorisation endpoint.
425         *                            May be {@code null} if the
426         *                            {@link #toHTTPRequest} method will not be
427         *                            used.
428         * @param rt                  The response type. Corresponds to the
429         *                            {@code response_type} parameter. Must not
430         *                            be {@code null}.
431         * @param rm                  The response mode. Corresponds to the
432         *                            optional {@code response_mode} parameter.
433         *                            Use of this parameter is not recommended
434         *                            unless a non-default response mode is
435         *                            requested (e.g. form_post).
436         * @param clientID            The client identifier. Corresponds to the
437         *                            {@code client_id} parameter. Must not be
438         *                            {@code null}.
439         * @param redirectURI         The redirection URI. Corresponds to the
440         *                            optional {@code redirect_uri} parameter.
441         *                            {@code null} if not specified.
442         * @param scope               The request scope. Corresponds to the
443         *                            optional {@code scope} parameter.
444         *                            {@code null} if not specified.
445         * @param state               The state. Corresponds to the recommended
446         *                            {@code state} parameter. {@code null} if
447         *                            not specified.
448         */
449        public AuthorizationRequest(final URI uri,
450                                    final ResponseType rt,
451                                    final ResponseMode rm,
452                                    final ClientID clientID,
453                                    final URI redirectURI,
454                                    final Scope scope,
455                                    final State state) {
456
457                this(uri, rt, rm, clientID, redirectURI, scope, state, null, null);
458        }
459
460
461        /**
462         * Creates a new authorisation request with PKCE support.
463         *
464         * @param uri                 The URI of the authorisation endpoint.
465         *                            May be {@code null} if the
466         *                            {@link #toHTTPRequest} method will not be
467         *                            used.
468         * @param rt                  The response type. Corresponds to the
469         *                            {@code response_type} parameter. Must not
470         *                            be {@code null}.
471         * @param rm                  The response mode. Corresponds to the
472         *                            optional {@code response_mode} parameter.
473         *                            Use of this parameter is not recommended
474         *                            unless a non-default response mode is
475         *                            requested (e.g. form_post).
476         * @param clientID            The client identifier. Corresponds to the
477         *                            {@code client_id} parameter. Must not be
478         *                            {@code null}.
479         * @param redirectURI         The redirection URI. Corresponds to the
480         *                            optional {@code redirect_uri} parameter.
481         *                            {@code null} if not specified.
482         * @param scope               The request scope. Corresponds to the
483         *                            optional {@code scope} parameter.
484         *                            {@code null} if not specified.
485         * @param state               The state. Corresponds to the recommended
486         *                            {@code state} parameter. {@code null} if
487         *                            not specified.
488         * @param codeChallenge       The code challenge for PKCE, {@code null}
489         *                            if not specified.
490         * @param codeChallengeMethod The code challenge method for PKCE,
491         *                            {@code null} if not specified.
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
503                this(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, Collections.<String,String>emptyMap());
504        }
505
506
507        /**
508         * Creates a new authorisation request with PKCE support and additional
509         * custom parameters.
510         *
511         * @param uri                 The URI of the authorisation endpoint.
512         *                            May be {@code null} if the
513         *                            {@link #toHTTPRequest} method will not be
514         *                            used.
515         * @param rt                  The response type. Corresponds to the
516         *                            {@code response_type} parameter. Must not
517         *                            be {@code null}.
518         * @param rm                  The response mode. Corresponds to the
519         *                            optional {@code response_mode} parameter.
520         *                            Use of this parameter is not recommended
521         *                            unless a non-default response mode is
522         *                            requested (e.g. form_post).
523         * @param clientID            The client identifier. Corresponds to the
524         *                            {@code client_id} parameter. Must not be
525         *                            {@code null}.
526         * @param redirectURI         The redirection URI. Corresponds to the
527         *                            optional {@code redirect_uri} parameter.
528         *                            {@code null} if not specified.
529         * @param scope               The request scope. Corresponds to the
530         *                            optional {@code scope} parameter.
531         *                            {@code null} if not specified.
532         * @param state               The state. Corresponds to the recommended
533         *                            {@code state} parameter. {@code null} if
534         *                            not specified.
535         * @param codeChallenge       The code challenge for PKCE, {@code null}
536         *                            if not specified.
537         * @param codeChallengeMethod The code challenge method for PKCE,
538         *                            {@code null} if not specified.
539         * @param customParams        Additional custom parameters, empty map
540         *                            or {@code null} if none.
541         */
542        public AuthorizationRequest(final URI uri,
543                                    final ResponseType rt,
544                                    final ResponseMode rm,
545                                    final ClientID clientID,
546                                    final URI redirectURI,
547                                    final Scope scope,
548                                    final State state,
549                                    final CodeChallenge codeChallenge,
550                                    final CodeChallengeMethod codeChallengeMethod,
551                                    final Map<String,String> customParams) {
552
553                super(uri);
554
555                if (rt == null)
556                        throw new IllegalArgumentException("The response type must not be null");
557
558                this.rt = rt;
559
560                this.rm = rm;
561
562
563                if (clientID == null)
564                        throw new IllegalArgumentException("The client ID must not be null");
565
566                this.clientID = clientID;
567
568
569                this.redirectURI = redirectURI;
570                this.scope = scope;
571                this.state = state;
572
573                this.codeChallenge = codeChallenge;
574                this.codeChallengeMethod = codeChallengeMethod;
575
576                if (MapUtils.isNotEmpty(customParams)) {
577                        this.customParams = Collections.unmodifiableMap(customParams);
578                } else {
579                        this.customParams = Collections.emptyMap();
580                }
581        }
582
583
584        /**
585         * Returns the registered (standard) OAuth 2.0 authorisation request
586         * parameter names.
587         *
588         * @return The registered OAuth 2.0 authorisation request parameter
589         *         names, as a unmodifiable set.
590         */
591        public static Set<String> getRegisteredParameterNames() {
592
593                return REGISTERED_PARAMETER_NAMES;
594        }
595
596
597        /**
598         * Gets the response type. Corresponds to the {@code response_type}
599         * parameter.
600         *
601         * @return The response type.
602         */
603        public ResponseType getResponseType() {
604        
605                return rt;
606        }
607
608
609        /**
610         * Gets the optional response mode. Corresponds to the optional
611         * {@code response_mode} parameter.
612         *
613         * @return The response mode, {@code null} if not specified.
614         */
615        public ResponseMode getResponseMode() {
616
617                return rm;
618        }
619
620
621        /**
622         * Returns the implied response mode, determined by the optional
623         * {@code response_mode} parameter, and if that isn't specified, by
624         * the {@code response_type}.
625         *
626         * @return The implied response mode.
627         */
628        public ResponseMode impliedResponseMode() {
629
630                if (rm != null) {
631                        return rm;
632                } else if (rt.impliesImplicitFlow()) {
633                        return ResponseMode.FRAGMENT;
634                } else {
635                        return ResponseMode.QUERY;
636                }
637        }
638
639
640        /**
641         * Gets the client identifier. Corresponds to the {@code client_id} 
642         * parameter.
643         *
644         * @return The client identifier.
645         */
646        public ClientID getClientID() {
647        
648                return clientID;
649        }
650
651
652        /**
653         * Gets the redirection URI. Corresponds to the optional 
654         * {@code redirection_uri} parameter.
655         *
656         * @return The redirection URI, {@code null} if not specified.
657         */
658        public URI getRedirectionURI() {
659        
660                return redirectURI;
661        }
662        
663        
664        /**
665         * Gets the scope. Corresponds to the optional {@code scope} parameter.
666         *
667         * @return The scope, {@code null} if not specified.
668         */
669        public Scope getScope() {
670        
671                return scope;
672        }
673        
674        
675        /**
676         * Gets the state. Corresponds to the recommended {@code state} 
677         * parameter.
678         *
679         * @return The state, {@code null} if not specified.
680         */
681        public State getState() {
682        
683                return state;
684        }
685
686
687        /**
688         * Returns the code challenge for PKCE.
689         *
690         * @return The code challenge, {@code null} if not specified.
691         */
692        public CodeChallenge getCodeChallenge() {
693
694                return codeChallenge;
695        }
696
697
698        /**
699         * Returns the code challenge method for PKCE.
700         *
701         * @return The code challenge method, {@code null} if not specified.
702         */
703        public CodeChallengeMethod getCodeChallengeMethod() {
704
705                return codeChallengeMethod;
706        }
707
708
709        /**
710         * Returns the additional custom parameters.
711         *
712         * @return The additional custom parameters as a unmodifiable map,
713         *         empty map if none.
714         */
715        public Map<String,String> getCustomParameters () {
716
717                return customParams;
718        }
719
720
721        /**
722         * Returns the specified custom parameter.
723         *
724         * @param name The parameter name. Must not be {@code null}.
725         *
726         * @return The parameter value, {@code null} if not specified.
727         */
728        public String getCustomParameter(final String name) {
729
730                return customParams.get(name);
731        }
732
733
734        /**
735         * Returns the parameters for this authorisation request.
736         *
737         * <p>Example parameters:
738         *
739         * <pre>
740         * response_type = code
741         * client_id     = s6BhdRkqt3
742         * state         = xyz
743         * redirect_uri  = https://client.example.com/cb
744         * </pre>
745         * 
746         * @return The parameters.
747         */
748        public Map<String,String> toParameters() {
749
750                Map <String,String> params = new LinkedHashMap<>();
751
752                // Put custom params first, so they may be overwritten by std params
753                params.putAll(customParams);
754                
755                params.put("response_type", rt.toString());
756                params.put("client_id", clientID.getValue());
757
758                if (rm != null) {
759                        params.put("response_mode", rm.getValue());
760                }
761
762                if (redirectURI != null)
763                        params.put("redirect_uri", redirectURI.toString());
764
765                if (scope != null)
766                        params.put("scope", scope.toString());
767                
768                if (state != null)
769                        params.put("state", state.getValue());
770
771                if (codeChallenge != null) {
772                        params.put("code_challenge", codeChallenge.getValue());
773
774                        if (codeChallengeMethod != null) {
775                                params.put("code_challenge_method", codeChallengeMethod.getValue());
776                        }
777                }
778
779                return params;
780        }
781        
782        
783        /**
784         * Returns the URI query string for this authorisation request.
785         *
786         * <p>Note that the '?' character preceding the query string in an URI
787         * is not included in the returned string.
788         *
789         * <p>Example URI query string:
790         *
791         * <pre>
792         * response_type=code
793         * &amp;client_id=s6BhdRkqt3
794         * &amp;state=xyz
795         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
796         * </pre>
797         * 
798         * @return The URI query string.
799         */
800        public String toQueryString() {
801                
802                return URLUtils.serializeParameters(toParameters());
803        }
804
805
806        /**
807         * Returns the complete URI representation for this authorisation
808         * request, consisting of the {@link #getEndpointURI authorization
809         * endpoint URI} with the {@link #toQueryString query string} appended.
810         *
811         * <p>Example URI:
812         *
813         * <pre>
814         * https://server.example.com/authorize?
815         * response_type=code
816         * &amp;client_id=s6BhdRkqt3
817         * &amp;state=xyz
818         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
819         * </pre>
820         *
821         * @return The URI representation.
822         */
823        public URI toURI() {
824
825                if (getEndpointURI() == null)
826                        throw new SerializeException("The authorization endpoint URI is not specified");
827
828                StringBuilder sb = new StringBuilder(getEndpointURI().toString());
829                sb.append('?');
830                sb.append(toQueryString());
831                try {
832                        return new URI(sb.toString());
833                } catch (URISyntaxException e) {
834                        throw new SerializeException("Couldn't append query string: " + e.getMessage(), e);
835                }
836        }
837        
838        
839        /**
840         * Returns the matching HTTP request.
841         *
842         * @param method The HTTP request method which can be GET or POST. Must
843         *               not be {@code null}.
844         *
845         * @return The HTTP request.
846         */
847        public HTTPRequest toHTTPRequest(final HTTPRequest.Method method) {
848                
849                if (getEndpointURI() == null)
850                        throw new SerializeException("The endpoint URI is not specified");
851                
852                HTTPRequest httpRequest;
853
854                URL endpointURL;
855
856                try {
857                        endpointURL = getEndpointURI().toURL();
858
859                } catch (MalformedURLException e) {
860
861                        throw new SerializeException(e.getMessage(), e);
862                }
863                
864                if (method.equals(HTTPRequest.Method.GET)) {
865
866                        httpRequest = new HTTPRequest(HTTPRequest.Method.GET, endpointURL);
867
868                } else if (method.equals(HTTPRequest.Method.POST)) {
869
870                        httpRequest = new HTTPRequest(HTTPRequest.Method.POST, endpointURL);
871
872                } else {
873
874                        throw new IllegalArgumentException("The HTTP request method must be GET or POST");
875                }
876                
877                httpRequest.setQuery(toQueryString());
878                
879                return httpRequest;
880        }
881        
882        
883        @Override
884        public HTTPRequest toHTTPRequest() {
885        
886                return toHTTPRequest(HTTPRequest.Method.GET);
887        }
888
889
890        /**
891         * Parses an authorisation request from the specified parameters.
892         *
893         * <p>Example parameters:
894         *
895         * <pre>
896         * response_type = code
897         * client_id     = s6BhdRkqt3
898         * state         = xyz
899         * redirect_uri  = https://client.example.com/cb
900         * </pre>
901         *
902         * @param params The parameters. Must not be {@code null}.
903         *
904         * @return The authorisation request.
905         *
906         * @throws ParseException If the parameters couldn't be parsed to an
907         *                        authorisation request.
908         */
909        public static AuthorizationRequest parse(final Map<String,String> params)
910                throws ParseException {
911
912                return parse(null, params);
913        }
914
915
916        /**
917         * Parses an authorisation request from the specified parameters.
918         *
919         * <p>Example parameters:
920         *
921         * <pre>
922         * response_type = code
923         * client_id     = s6BhdRkqt3
924         * state         = xyz
925         * redirect_uri  = https://client.example.com/cb
926         * </pre>
927         *
928         * @param uri    The URI of the authorisation endpoint. May be
929         *               {@code null} if the {@link #toHTTPRequest()} method
930         *               will not be used.
931         * @param params The parameters. Must not be {@code null}.
932         *
933         * @return The authorisation request.
934         *
935         * @throws ParseException If the parameters couldn't be parsed to an
936         *                        authorisation request.
937         */
938        public static AuthorizationRequest parse(final URI uri, final Map<String,String> params)
939                throws ParseException {
940
941                // Parse mandatory client ID first
942                String v = params.get("client_id");
943
944                if (StringUtils.isBlank(v)) {
945                        String msg = "Missing \"client_id\" parameter";
946                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
947                }
948
949                ClientID clientID = new ClientID(v);
950
951
952                // Parse optional redirection URI second
953                v = params.get("redirect_uri");
954
955                URI redirectURI = null;
956
957                if (StringUtils.isNotBlank(v)) {
958
959                        try {
960                                redirectURI = new URI(v);
961
962                        } catch (URISyntaxException e) {
963                                String msg = "Invalid \"redirect_uri\" parameter: " + e.getMessage();
964                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
965                                                         clientID, null, null, null, e);
966                        }
967                }
968
969
970                // Parse optional state third
971                State state = State.parse(params.get("state"));
972
973
974                // Parse mandatory response type
975                v = params.get("response_type");
976
977                ResponseType rt;
978
979                try {
980                        rt = ResponseType.parse(v);
981
982                } catch (ParseException e) {
983                        // Only cause
984                        String msg = "Missing \"response_type\" parameter";
985                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg),
986                                                 clientID, redirectURI, null, state, e);
987                }
988
989
990                // Parse the optional response mode
991                v = params.get("response_mode");
992
993                ResponseMode rm = null;
994
995                if (StringUtils.isNotBlank(v)) {
996                        rm = new ResponseMode(v);
997                }
998
999
1000                // Parse optional scope
1001                v = params.get("scope");
1002
1003                Scope scope = null;
1004
1005                if (StringUtils.isNotBlank(v))
1006                        scope = Scope.parse(v);
1007
1008
1009                // Parse optional code challenge and method for PKCE
1010                CodeChallenge codeChallenge = null;
1011                CodeChallengeMethod codeChallengeMethod = null;
1012
1013                v = params.get("code_challenge");
1014
1015                if (StringUtils.isNotBlank(v))
1016                        codeChallenge = CodeChallenge.parse(v);
1017
1018                if (codeChallenge != null) {
1019
1020                        v = params.get("code_challenge_method");
1021
1022                        if (StringUtils.isNotBlank(v))
1023                                codeChallengeMethod = CodeChallengeMethod.parse(v);
1024                }
1025
1026                // Parse additional custom parameters
1027                Map<String,String> customParams = null;
1028
1029                for (Map.Entry<String,String> p: params.entrySet()) {
1030
1031                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) {
1032                                // We have a custom parameter
1033                                if (customParams == null) {
1034                                        customParams = new HashMap<>();
1035                                }
1036                                customParams.put(p.getKey(), p.getValue());
1037                        }
1038                }
1039
1040
1041                return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams);
1042        }
1043
1044
1045        /**
1046         * Parses an authorisation request from the specified URI query string.
1047         *
1048         * <p>Example URI query string:
1049         *
1050         * <pre>
1051         * response_type=code
1052         * &amp;client_id=s6BhdRkqt3
1053         * &amp;state=xyz
1054         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1055         * </pre>
1056         *
1057         * @param query The URI query string. Must not be {@code null}.
1058         *
1059         * @return The authorisation request.
1060         *
1061         * @throws ParseException If the query string couldn't be parsed to an
1062         *                        authorisation request.
1063         */
1064        public static AuthorizationRequest parse(final String query)
1065                throws ParseException {
1066
1067                return parse(null, URLUtils.parseParameters(query));
1068        }
1069        
1070        
1071        /**
1072         * Parses an authorisation request from the specified URI query string.
1073         *
1074         * <p>Example URI query string:
1075         *
1076         * <pre>
1077         * response_type=code
1078         * &amp;client_id=s6BhdRkqt3
1079         * &amp;state=xyz
1080         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1081         * </pre>
1082         *
1083         * @param uri   The URI of the authorisation endpoint. May be 
1084         *              {@code null} if the {@link #toHTTPRequest()} method
1085         *              will not be used.
1086         * @param query The URI query string. Must not be {@code null}.
1087         *
1088         * @return The authorisation request.
1089         *
1090         * @throws ParseException If the query string couldn't be parsed to an 
1091         *                        authorisation request.
1092         */
1093        public static AuthorizationRequest parse(final URI uri, final String query)
1094                throws ParseException {
1095        
1096                return parse(uri, URLUtils.parseParameters(query));
1097        }
1098
1099
1100        /**
1101         * Parses an authorisation request from the specified URI.
1102         *
1103         * <p>Example URI:
1104         *
1105         * <pre>
1106         * https://server.example.com/authorize?
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. Must not be {@code null}.
1114         *
1115         * @return The authorisation request.
1116         *
1117         * @throws ParseException If the URI couldn't be parsed to an
1118         *                        authorisation request.
1119         */
1120        public static AuthorizationRequest parse(final URI uri)
1121                throws ParseException {
1122
1123                return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery()));
1124        }
1125        
1126        
1127        /**
1128         * Parses an authorisation request from the specified HTTP request.
1129         *
1130         * <p>Example HTTP request (GET):
1131         *
1132         * <pre>
1133         * https://server.example.com/authorize?
1134         * response_type=code
1135         * &amp;client_id=s6BhdRkqt3
1136         * &amp;state=xyz
1137         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
1138         * </pre>
1139         *
1140         * @param httpRequest The HTTP request. Must not be {@code null}.
1141         *
1142         * @return The authorisation request.
1143         *
1144         * @throws ParseException If the HTTP request couldn't be parsed to an 
1145         *                        authorisation request.
1146         */
1147        public static AuthorizationRequest parse(final HTTPRequest httpRequest) 
1148                throws ParseException {
1149                
1150                String query = httpRequest.getQuery();
1151                
1152                if (query == null)
1153                        throw new ParseException("Missing URI query string");
1154
1155                try {
1156                        return parse(URIUtils.getBaseURI(httpRequest.getURL().toURI()), query);
1157
1158                } catch (URISyntaxException e) {
1159
1160                        throw new ParseException(e.getMessage(), e);
1161                }
1162        }
1163}