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