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 * &client_id=s6BhdRkqt3
035 * &state=xyz
036 * &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 * &client_id=s6BhdRkqt3
251 * &state=xyz
252 * &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 * &client_id=s6BhdRkqt3
407 * &state=xyz
408 * &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 * &client_id=s6BhdRkqt3
434 * &state=xyz
435 * &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 }