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 extends AbstractRequest {
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 uri         The URI of the authorisation endpoint. May be 
084             *                    {@code null} if the {@link #toHTTPRequest()}
085             *                    method will not be used.
086             * @param rt          The response type. 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 URL uri,
094                                        final ResponseType rt,
095                                        final ClientID clientID) {
096    
097                    this(uri, rt, clientID, null, null, null);
098            }
099            
100            
101            /**
102             * Creates a new authorisation request.
103             *
104             *  @param uri        The URI of the authorisation endpoint. May be 
105             *                    {@code null} if the {@link #toHTTPRequest()}
106             *                    method will not be used.
107             * @param rt          The response type. Corresponds to the 
108             *                    {@code response_type} parameter. Must not be
109             *                    {@code null}.
110             * @param clientID    The client identifier. Corresponds to the
111             *                    {@code client_id} parameter. Must not be 
112             *                    {@code null}.
113             * @param redirectURI The redirection URI. Corresponds to the optional
114             *                    {@code redirect_uri} parameter. {@code null} if
115             *                    not specified.
116             * @param scope       The request scope. Corresponds to the optional
117             *                    {@code scope} parameter. {@code null} if not
118             *                    specified.
119             * @param state       The state. Corresponds to the recommended 
120             *                    {@code state} parameter. {@code null} if not 
121             *                    specified.
122             */
123            public AuthorizationRequest(final URL uri,
124                                        final ResponseType rt,
125                                        final ClientID clientID,
126                                        final URL redirectURI,
127                                        final Scope scope,
128                                        final State state) {
129    
130                    super(uri);
131                    
132                    if (rt == null)
133                            throw new IllegalArgumentException("The response type must not be null");
134                    
135                    this.rt = rt;
136    
137    
138                    if (clientID == null)
139                            throw new IllegalArgumentException("The client ID must not be null");
140                            
141                    this.clientID = clientID;
142                    
143                    
144                    this.redirectURI = redirectURI;
145                    this.scope = scope;
146                    this.state = state;
147            }
148            
149            
150            /**
151             * Gets the response type. Corresponds to the {@code response_type}
152             * parameter.
153             *
154             * @return The response type.
155             */
156            public ResponseType getResponseType() {
157            
158                    return rt;
159            }
160    
161    
162            /**
163             * Gets the client identifier. Corresponds to the {@code client_id} 
164             * parameter.
165             *
166             * @return The client identifier.
167             */
168            public ClientID getClientID() {
169            
170                    return clientID;
171            }
172    
173    
174            /**
175             * Gets the redirection URI. Corresponds to the optional 
176             * {@code redirection_uri} parameter.
177             *
178             * @return The redirection URI, {@code null} if not specified.
179             */
180            public URL getRedirectURI() {
181            
182                    return redirectURI;
183            }
184            
185            
186            /**
187             * Gets the scope. Corresponds to the optional {@code scope} parameter.
188             *
189             * @return The scope, {@code null} if not specified.
190             */
191            public Scope getScope() {
192            
193                    return scope;
194            }
195            
196            
197            /**
198             * Gets the state. Corresponds to the recommended {@code state} 
199             * parameter.
200             *
201             * @return The state, {@code null} if not specified.
202             */
203            public State getState() {
204            
205                    return state;
206            }
207    
208    
209            /**
210             * Returns the parameters for this authorisation request.
211             *
212             * <p>Example parameters:
213             *
214             * <pre>
215             * response_type = code
216             * client_id     = s6BhdRkqt3
217             * state         = xyz
218             * redirect_uri  = https://client.example.com/cb
219             * </pre>
220             * 
221             * @return The parameters.
222             *
223             * @throws SerializeException If this authorisation request couldn't be
224             *                            serialised to an parameters map.
225             */
226            public Map<String,String> toParameters()
227                    throws SerializeException {
228    
229                    Map <String,String> params = new LinkedHashMap<String,String>();
230                    
231                    params.put("response_type", rt.toString());
232                    params.put("client_id", clientID.getValue());
233    
234                    if (redirectURI != null)
235                            params.put("redirect_uri", redirectURI.toString());
236    
237                    if (scope != null)
238                            params.put("scope", scope.toString());
239                    
240                    if (state != null)
241                            params.put("state", state.getValue());
242    
243                    return params;
244            }
245            
246            
247            /**
248             * Returns the URL query string for this authorisation request.
249             *
250             * <p>Note that the '?' character preceding the query string in an URL
251             * is not included in the returned string.
252             *
253             * <p>Example URL query string:
254             *
255             * <pre>
256             * response_type=code
257             * &amp;client_id=s6BhdRkqt3
258             * &amp;state=xyz
259             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
260             * </pre>
261             * 
262             * @return The URL query string.
263             *
264             * @throws SerializeException If this authorisation request couldn't be
265             *                            serialised to an URL query string.
266             */
267            public String toQueryString()
268                    throws SerializeException {
269                    
270                    return URLUtils.serializeParameters(toParameters());
271            }
272            
273            
274            /**
275             * Returns the matching HTTP request.
276             *
277             * @param method The HTTP request method which can be GET or POST. Must
278             *               not be {@code null}.
279             *
280             * @return The HTTP request.
281             *
282             * @throws SerializeException If the authorisation request message
283             *                            couldn't be serialised to an HTTP  
284             *                            request.
285             */
286            public HTTPRequest toHTTPRequest(final HTTPRequest.Method method)
287                    throws SerializeException {
288                    
289                    if (getURI() == null)
290                            throw new SerializeException("The endpoint URI is not specified");
291                    
292                    HTTPRequest httpRequest;
293                    
294                    if (method.equals(HTTPRequest.Method.GET)) {
295    
296                            httpRequest = new HTTPRequest(HTTPRequest.Method.GET, getURI());
297    
298                    } else if (method.equals(HTTPRequest.Method.POST)) {
299    
300                            httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getURI());
301    
302                    } else {
303    
304                            throw new IllegalArgumentException("The HTTP request method must be GET or POST");
305                    }
306                    
307                    httpRequest.setQuery(toQueryString());
308                    
309                    return httpRequest;
310            }
311            
312            
313            @Override
314            public HTTPRequest toHTTPRequest()
315                    throws SerializeException {
316            
317                    return toHTTPRequest(HTTPRequest.Method.GET);
318            }
319    
320    
321            /**
322             * Parses an authorisation request from the specified parameters.
323             *
324             * <p>Example parameters:
325             *
326             * <pre>
327             * response_type = code
328             * client_id     = s6BhdRkqt3
329             * state         = xyz
330             * redirect_uri  = https://client.example.com/cb
331             * </pre>
332             *
333             * @param uri    The URI of the authorisation endpoint. May be 
334             *               {@code null} if the {@link #toHTTPRequest()} method 
335             *               will not be used.
336             * @param params The parameters. Must not be {@code null}.
337             *
338             * @return The authorisation request.
339             *
340             * @throws ParseException If the parameters couldn't be parsed to an
341             *                        authorisation request.
342             */
343            public static AuthorizationRequest parse(final URL uri, final Map<String,String> params)
344                    throws ParseException {
345    
346                    // Parse mandatory client ID first
347                    String v = params.get("client_id");
348                    
349                    if (StringUtils.isBlank(v))
350                            throw new ParseException("Missing \"client_id\" parameter", 
351                                                     OAuth2Error.INVALID_REQUEST);
352    
353                    ClientID clientID = new ClientID(v);
354    
355    
356                    // Parse optional redirect URI second
357                    v = params.get("redirect_uri");
358    
359                    URL redirectURI = null;
360    
361                    if (StringUtils.isNotBlank(v)) {
362                            
363                            try {
364                                    redirectURI = new URL(v);
365                                    
366                            } catch (MalformedURLException e) {
367                            
368                                    throw new ParseException("Invalid \"redirect_uri\" parameter: " + e.getMessage(), 
369                                                             OAuth2Error.INVALID_REQUEST, e);
370                            }
371                    }
372    
373    
374                    // Parse optional state third
375                    State state = State.parse(params.get("state"));
376                    
377    
378                    // Parse mandatory response type
379                    v = params.get("response_type");
380                    
381                    ResponseType rt = null;
382                    
383                    try {
384                            rt = ResponseType.parse(v);
385                    
386                    } catch (ParseException e) {
387                            
388                            throw new ParseException(e.getMessage(), 
389                                                     OAuth2Error.UNSUPPORTED_RESPONSE_TYPE, 
390                                                     redirectURI, state, e);
391                    }
392                            
393                    
394                    // Parse optional scope
395                    v = params.get("scope");
396    
397                    Scope scope = null;
398                    
399                    if (StringUtils.isNotBlank(v))
400                            scope = Scope.parse(v);
401    
402    
403                    return new AuthorizationRequest(uri, rt, clientID, redirectURI, scope, state);
404    
405            }
406            
407            
408            /**
409             * Parses an authorisation request from the specified URL query string.
410             *
411             * <p>Example URL query string:
412             *
413             * <pre>
414             * response_type=code
415             * &amp;client_id=s6BhdRkqt3
416             * &amp;state=xyz
417             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
418             * </pre>
419             *
420             * @param uri   The URI of the authorisation endpoint. May be 
421             *              {@code null} if the {@link #toHTTPRequest()} method
422             *              will not be used.
423             * @param query The URL query string. Must not be {@code null}.
424             *
425             * @return The authorisation request.
426             *
427             * @throws ParseException If the query string couldn't be parsed to an 
428             *                        authorisation request.
429             */
430            public static AuthorizationRequest parse(final URL uri, final String query)
431                    throws ParseException {
432            
433                    return parse(uri, URLUtils.parseParameters(query));
434            }
435            
436            
437            /**
438             * Parses an authorisation request from the specified HTTP request.
439             *
440             * <p>Example HTTP request (GET):
441             *
442             * <pre>
443             * https://server.example.com/authorize?
444             * response_type=code
445             * &amp;client_id=s6BhdRkqt3
446             * &amp;state=xyz
447             * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
448             * </pre>
449             *
450             * @param httpRequest The HTTP request. Must not be {@code null}.
451             *
452             * @return The authorisation request.
453             *
454             * @throws ParseException If the HTTP request couldn't be parsed to an 
455             *                        authorisation request.
456             */
457            public static AuthorizationRequest parse(final HTTPRequest httpRequest) 
458                    throws ParseException {
459                    
460                    String query = httpRequest.getQuery();
461                    
462                    if (query == null)
463                            throw new ParseException("Missing URL query string");
464                    
465                    return parse(URLUtils.getBaseURL(httpRequest.getURL()), query);
466            }
467    }