001    package com.nimbusds.oauth2.sdk;
002    
003    
004    import java.net.MalformedURLException;
005    import java.net.URL;
006    import java.util.LinkedHashMap;
007    import java.util.Map;
008    
009    import net.jcip.annotations.Immutable;
010    
011    import org.apache.commons.lang3.StringUtils;
012    
013    import com.nimbusds.oauth2.sdk.id.ClientID;
014    import com.nimbusds.oauth2.sdk.id.State;
015    import com.nimbusds.oauth2.sdk.http.HTTPRequest;
016    import com.nimbusds.oauth2.sdk.util.URLUtils;
017    
018    
019    /**
020     * Authorisation request. Used to authenticate an end-user and request the
021     * end-user's consent to grant the client access to a protected resource. 
022     * This class is immutable.
023     *
024     * <p>Extending classes may define additional request parameters as well as 
025     * enforce tighter requirements on the base parameters.
026     *
027     * <p>Example HTTP request:
028     *
029     * <pre>
030     * https://server.example.com/authorize?
031     * response_type=code
032     * &amp;client_id=s6BhdRkqt3
033     * &amp;state=xyz
034     * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
035     * </pre>
036     *
037     * <p>Related specifications:
038     *
039     * <ul>
040     *     <li>OAuth 2.0 (RFC 6749), sections 4.1.1 and 4.2.1.
041     * </ul>
042     *
043     * @author Vladimir Dzhuvinov
044     */
045    @Immutable
046    public class AuthorizationRequest implements Request {
047    
048    
049            /**
050             * The response type (required).
051             */
052            private final ResponseType rt;
053    
054    
055            /**
056             * The client identifier (required).
057             */
058            private final ClientID clientID;
059    
060    
061            /**
062             * The redirection URI where the response will be sent (optional). 
063             */
064            private final URL redirectURI;
065            
066            
067            /**
068             * The scope (optional).
069             */
070            private final Scope scope;
071            
072            
073            /**
074             * The opaque value to maintain state between the request and the 
075             * callback (recommended).
076             */
077            private final State state;
078    
079    
080            /**
081             * Creates a new minimal authorisation request.
082             *
083             * @param rt          The response type. Corresponds to the 
084             *                    {@code response_type} parameter. Must not be
085             *                    {@code null}.
086             * @param clientID    The client identifier. Corresponds to the
087             *                    {@code client_id} parameter. Must not be 
088             *                    {@code null}.
089             */
090            public AuthorizationRequest(final ResponseType rt,
091                                        final ClientID clientID) {
092    
093                    this(rt, clientID, null, null, null);
094            }
095            
096            
097            /**
098             * Creates a new authorisation request.
099             *
100             * @param rt          The response type. Corresponds to the 
101             *                    {@code response_type} parameter. Must not be
102             *                    {@code null}.
103             * @param clientID    The client identifier. Corresponds to the
104             *                    {@code client_id} parameter. Must not be 
105             *                    {@code null}.
106             * @param redirectURI The redirection URI. Corresponds to the optional
107             *                    {@code redirect_uri} parameter. {@code null} if
108             *                    not specified.
109             * @param scope       The request scope. Corresponds to the optional
110             *                    {@code scope} parameter. {@code null} if not
111             *                    specified.
112             * @param state       The state. Corresponds to the recommended 
113             *                    {@code state} parameter. {@code null} if not 
114             *                    specified.
115             */
116            public AuthorizationRequest(final ResponseType rt,
117                                        final ClientID clientID,
118                                        final URL redirectURI,
119                                        final Scope scope,
120                                        final State state) {
121    
122                    if (rt == null)
123                            throw new IllegalArgumentException("The response type must not be null");
124                    
125                    this.rt = rt;
126    
127    
128                    if (clientID == null)
129                            throw new IllegalArgumentException("The client ID must not be null");
130                            
131                    this.clientID = clientID;
132                    
133                    
134                    this.redirectURI = redirectURI;
135                    this.scope = scope;
136                    this.state = state;
137            }
138            
139            
140            /**
141             * Gets the response type. Corresponds to the {@code response_type}
142             * parameter.
143             *
144             * @return The response type.
145             */
146            public ResponseType getResponseType() {
147            
148                    return rt;
149            }
150    
151    
152            /**
153             * Gets the client identifier. Corresponds to the {@code client_id} 
154             * parameter.
155             *
156             * @return The client identifier.
157             */
158            public ClientID getClientID() {
159            
160                    return clientID;
161            }
162    
163    
164            /**
165             * Gets the redirection URI. Corresponds to the optional 
166             * {@code redirection_uri} parameter.
167             *
168             * @return The redirection URI, {@code null} if not specified.
169             */
170            public URL getRedirectURI() {
171            
172                    return redirectURI;
173            }
174            
175            
176            /**
177             * Gets the scope. Corresponds to the optional {@code scope} parameter.
178             *
179             * @return The scope, {@code null} if not specified.
180             */
181            public Scope getScope() {
182            
183                    return scope;
184            }
185            
186            
187            /**
188             * Gets the state. Corresponds to the recommended {@code state} 
189             * parameter.
190             *
191             * @return The state, {@code null} if not specified.
192             */
193            public State getState() {
194            
195                    return state;
196            }
197    
198    
199            /**
200             * Returns the parameters for this authorisation request.
201             *
202             * <p>Example parameters:
203             *
204             * <pre>
205             * response_type = code
206             * client_id     = s6BhdRkqt3
207             * state         = xyz
208             * redirect_uri  = https://client.example.com/cb
209             * </pre>
210             * 
211             * @return The parameters.
212             *
213             * @throws SerializeException If this authorisation request couldn't be
214             *                            serialised to an parameters map.
215             */
216            public Map<String,String> toParameters()
217                    throws SerializeException {
218    
219                    Map <String,String> params = new LinkedHashMap<String,String>();
220                    
221                    params.put("response_type", rt.toString());
222                    params.put("client_id", clientID.getValue());
223    
224                    if (redirectURI != null)
225                            params.put("redirect_uri", redirectURI.toString());
226    
227                    if (scope != null)
228                            params.put("scope", scope.toString());
229                    
230                    if (state != null)
231                            params.put("state", state.getValue());
232    
233                    return params;
234            }
235            
236            
237            /**
238             * Returns the URL query string for this authorisation request.
239             *
240             * <p>Note that the '?' character preceding the query string in an URL
241             * is not included in the returned string.
242             *
243             * <p>Example URL query string:
244             *
245             * <pre>
246             * response_type=code
247             * &amp;client_id=s6BhdRkqt3
248             * &amp;state=xyz
249             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
250             * </pre>
251             * 
252             * @return The URL query string.
253             *
254             * @throws SerializeException If this authorisation request couldn't be
255             *                            serialised to an URL query string.
256             */
257            public String toQueryString()
258                    throws SerializeException {
259                    
260                    return URLUtils.serializeParameters(toParameters());
261            }
262            
263            
264            /**
265             * Returns the matching HTTP request.
266             *
267             * @param method The HTTP request method which can be GET or POST. Must
268             *               not be {@code null}.
269             * @param url    The URL of the HTTP endpoint for which the request is
270             *               intended. Must not be {@code null}.
271             *
272             * @return The HTTP request.
273             *
274             * @throws SerializeException If the authorisation request message
275             *                            couldn't be serialised to an HTTP  
276             *                            request.
277             */
278            public HTTPRequest toHTTPRequest(final HTTPRequest.Method method, final URL url)
279                    throws SerializeException {
280                    
281                    HTTPRequest httpRequest;
282                    
283                    if (method.equals(HTTPRequest.Method.GET)) {
284    
285                            httpRequest = new HTTPRequest(HTTPRequest.Method.GET, url);
286    
287                    } else if (method.equals(HTTPRequest.Method.POST)) {
288    
289                            httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url);
290    
291                    } else {
292    
293                            throw new IllegalArgumentException("The HTTP request method must be GET or POST");
294                    }
295                    
296                    httpRequest.setQuery(toQueryString());
297                    
298                    return httpRequest;
299            }
300            
301            
302            @Override
303            public HTTPRequest toHTTPRequest(final URL url)
304                    throws SerializeException {
305            
306                    return toHTTPRequest(HTTPRequest.Method.GET, url);
307            }
308    
309    
310            /**
311             * Parses an authorisation request from the specified parameters.
312             *
313             * <p>Example parameters:
314             *
315             * <pre>
316             * response_type = code
317             * client_id     = s6BhdRkqt3
318             * state         = xyz
319             * redirect_uri  = https://client.example.com/cb
320             * </pre>
321             *
322             * @param params The parameters. Must not be {@code null}.
323             *
324             * @return The authorisation request.
325             *
326             * @throws ParseException If the parameters couldn't be parsed to an
327             *                        authorisation request.
328             */
329            public static AuthorizationRequest parse(final Map<String,String> params)
330                    throws ParseException {
331    
332                    // Parse mandatory client ID first
333                    String v = params.get("client_id");
334                    
335                    if (StringUtils.isBlank(v))
336                            throw new ParseException("Missing \"client_id\" parameter", 
337                                                     OAuth2Error.INVALID_REQUEST);
338    
339                    ClientID clientID = new ClientID(v);
340    
341    
342                    // Parse optional redirect URI second
343                    v = params.get("redirect_uri");
344    
345                    URL redirectURI = null;
346    
347                    if (StringUtils.isNotBlank(v)) {
348                            
349                            try {
350                                    redirectURI = new URL(v);
351                                    
352                            } catch (MalformedURLException e) {
353                            
354                                    throw new ParseException("Invalid \"redirect_uri\" parameter: " + e.getMessage(), 
355                                                             OAuth2Error.INVALID_REQUEST, e);
356                            }
357                    }
358    
359    
360                    // Parse optional state third
361                    State state = State.parse(params.get("state"));
362                    
363    
364                    // Parse mandatory response type
365                    v = params.get("response_type");
366                    
367                    ResponseType rt = null;
368                    
369                    try {
370                            rt = ResponseType.parse(v);
371                    
372                    } catch (ParseException e) {
373                            
374                            throw new ParseException(e.getMessage(), 
375                                                     OAuth2Error.UNSUPPORTED_RESPONSE_TYPE, 
376                                                     redirectURI, state, e);
377                    }
378                            
379                    
380                    // Parse optional scope
381                    v = params.get("scope");
382    
383                    Scope scope = null;
384                    
385                    if (StringUtils.isNotBlank(v))
386                            scope = Scope.parse(v);
387    
388    
389                    return new AuthorizationRequest(rt, clientID, redirectURI, scope, state);
390    
391            }
392            
393            
394            /**
395             * Parses an authorisation request from the specified URL query string.
396             *
397             * <p>Example URL query string:
398             *
399             * <pre>
400             * response_type=code
401             * &amp;client_id=s6BhdRkqt3
402             * &amp;state=xyz
403             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
404             * </pre>
405             *
406             * @param query The URL query string. Must not be {@code null}.
407             *
408             * @return The authorisation request.
409             *
410             * @throws ParseException If the query string couldn't be parsed to an 
411             *                        authorisation request.
412             */
413            public static AuthorizationRequest parse(final String query)
414                    throws ParseException {
415            
416                    return parse(URLUtils.parseParameters(query));
417            }
418            
419            
420            /**
421             * Parses an authorisation request from the specified HTTP request.
422             *
423             * <p>Example HTTP request (GET):
424             *
425             * <pre>
426             * https://server.example.com/authorize?
427             * response_type=code
428             * &amp;client_id=s6BhdRkqt3
429             * &amp;state=xyz
430             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
431             * </pre>
432             *
433             * @param httpRequest The HTTP request. Must not be {@code null}.
434             *
435             * @return The authorisation request.
436             *
437             * @throws ParseException If the HTTP request couldn't be parsed to an 
438             *                        authorisation request.
439             */
440            public static AuthorizationRequest parse(final HTTPRequest httpRequest) 
441                    throws ParseException {
442                    
443                    String query = httpRequest.getQuery();
444                    
445                    if (query == null)
446                            throw new ParseException("Missing URL query string");
447                    
448                    return parse(query);
449            }
450    }