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 }