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 com.nimbusds.oauth2.sdk.auth.ClientAuthentication; 012 import com.nimbusds.oauth2.sdk.id.ClientID; 013 import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 014 import com.nimbusds.oauth2.sdk.http.HTTPRequest; 015 import com.nimbusds.oauth2.sdk.util.URLUtils; 016 017 018 /** 019 * Access token request to the Token endpoint. Used to obtain an 020 * {@link com.nimbusds.oauth2.sdk.token.AccessToken access token} and an 021 * optional {@link com.nimbusds.oauth2.sdk.token.RefreshToken refresh token} 022 * from the authorisation server. This class is immutable. 023 * 024 * <p>Supported authorisation grant types: 025 * 026 * <ul> 027 * <li>{@link GrantType#AUTHORIZATION_CODE Authorisation code} 028 * <li>{@link GrantType#PASSWORD Resource owner password credentials} 029 * <li>{@link GrantType#CLIENT_CREDENTIALS Client credentials} 030 * </ul> 031 * 032 * <p>Example HTTP request, with 033 * {@link com.nimbusds.oauth2.sdk.auth.ClientSecretBasic client secret basic} 034 * authentication: 035 * 036 * <pre> 037 * POST /token HTTP/1.1 038 * Host: server.example.com 039 * Content-Type: application/x-www-form-urlencoded 040 * Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW 041 * 042 * grant_type=authorization_code 043 * &code=SplxlOBeZQQYbYS6WxSbIA 044 * &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb 045 * </pre> 046 * 047 * <p>Related specifications: 048 * 049 * <ul> 050 * <li>OAuth 2.0 (RFC 6749), sections 4.1.3, 4.3.2 and 4.4.2. 051 * </ul> 052 * 053 * @author Vladimir Dzhuvinov 054 * @version $version$ (2013-05-10) 055 */ 056 @Immutable 057 public final class AccessTokenRequest extends TokenRequest { 058 059 060 // Authorisation code grant 061 062 /** 063 * The authorisation code received from the authorisation server. 064 */ 065 private final AuthorizationCode code; 066 067 068 /** 069 * The conditionally required redirect URI in the initial authorisation 070 * request. 071 */ 072 private final URL redirectURI; 073 074 075 /** 076 * The conditionally required client ID. 077 */ 078 private final ClientID clientID; 079 080 081 // Password credentials grant 082 083 /** 084 * The username. 085 */ 086 private final String username; 087 088 089 /** 090 * The password. 091 */ 092 private final String password; 093 094 095 // For password + client credentials grant 096 097 /** 098 * The access scope. 099 */ 100 private final Scope scope; 101 102 103 /** 104 * Creates a new unauthenticated access token request, using an 105 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 106 * 107 * @param code The authorisation code received from the 108 * authorisation server. Must not be {@code null}. 109 * @param redirectURI The redirect URI, may be {@code null} if 110 * specified in the initial authorisation request. 111 * @param clientID The client identifier. Must not be {@code null}. 112 */ 113 public AccessTokenRequest(final AuthorizationCode code, 114 final URL redirectURI, 115 final ClientID clientID) { 116 117 super(GrantType.AUTHORIZATION_CODE, null); 118 119 if (code == null) 120 throw new IllegalArgumentException("The authorization code must not be null"); 121 122 this.code = code; 123 124 125 this.redirectURI = redirectURI; 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 username = null; 135 password = null; 136 scope = null; 137 } 138 139 140 /** 141 * Creates a new authenticated access token request, using an 142 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 143 * 144 * @param code The authorisation code received from the 145 * authorisation server. Must not be {@code null}. 146 * @param redirectURI The redirect URI, may be {@code null} if not 147 * specified in the initial authorisation request. 148 * @param clientAuth The client authentication. Must not be 149 * {@code null}. 150 */ 151 public AccessTokenRequest(final AuthorizationCode code, 152 final URL redirectURI, 153 final ClientAuthentication clientAuth) { 154 155 super(GrantType.AUTHORIZATION_CODE, clientAuth); 156 157 if (code == null) 158 throw new IllegalArgumentException("The authorization code must not be null"); 159 160 this.code = code; 161 162 this.redirectURI = redirectURI; 163 164 if (clientAuth == null) 165 throw new IllegalArgumentException("The client authentication must not be null"); 166 167 168 clientID = null; 169 username = null; 170 password = null; 171 scope = null; 172 } 173 174 175 /** 176 * Creates a new authenticated access token request, using a 177 * {@link GrantType#PASSWORD resource owner password credentials grant}. 178 * 179 * @param username The resource owner username. Must not be 180 * {@code null}. 181 * @param password The resource owner password. Must not be 182 * {@code null}. 183 * @param scope The scope of the access request, {@code null} if not 184 * specified. 185 */ 186 public AccessTokenRequest(final String username, 187 final String password, 188 final Scope scope) { 189 190 super(GrantType.PASSWORD, null); 191 192 if (username == null) 193 throw new IllegalArgumentException("The username must not be null"); 194 195 this.username = username; 196 197 198 if (password == null) 199 throw new IllegalArgumentException("The password must not be null"); 200 201 this.password = password; 202 203 this.scope = scope; 204 205 code = null; 206 redirectURI = null; 207 clientID = null; 208 } 209 210 211 /** 212 * Creates a new authenticated access token request, using a 213 * {@link GrantType#CLIENT_CREDENTIALS client credentials grant}. 214 * 215 * @param scope The scope of the access request, {@code null} if 216 * not specified. 217 * @param clientAuth The client authentication. Must not be 218 * {@code null}. 219 */ 220 public AccessTokenRequest(final Scope scope, 221 final ClientAuthentication clientAuth) { 222 223 super(GrantType.CLIENT_CREDENTIALS, null); 224 225 this.scope = scope; 226 227 if (clientAuth == null) 228 throw new IllegalArgumentException("The client authentication must not be null"); 229 230 code = null; 231 redirectURI = null; 232 clientID = null; 233 username = null; 234 password = null; 235 } 236 237 238 /** 239 * Gets the authorisation code. Applies to requests using an 240 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 241 * 242 * @return The authorisation code, {@code null} if not specified. 243 */ 244 public AuthorizationCode getAuthorizationCode() { 245 246 return code; 247 } 248 249 250 /** 251 * Gets the redirect URI. Applies to requests using an 252 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant} 253 * 254 * @return The redirect URI, {@code null} if not specified. 255 */ 256 public URL getRedirectURI() { 257 258 return redirectURI; 259 } 260 261 262 /** 263 * Gets the client identifier. Applies to requests using an 264 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 265 * 266 * @return The client identifier, {@code null} if not specified. 267 */ 268 public ClientID getClientID() { 269 270 return clientID; 271 } 272 273 274 /** 275 * Gets the resource owner username. Applies to requests using a 276 * {@link GrantType#PASSWORD resource owner password credentials 277 * grant}. 278 * 279 * @return The resource owner username, {@code null} if not specified. 280 */ 281 public String getUsername() { 282 283 return username; 284 } 285 286 287 /** 288 * Gets the resource owner password. Applies to requests using a 289 * {@link GrantType#PASSWORD resource owner password credentials 290 * grant}. 291 * 292 * @return The resource owner password, {@code null} if not specified. 293 */ 294 public String getPassword() { 295 296 return password; 297 } 298 299 300 /** 301 * Gets the access scope. Applies to requests using a 302 * {@link GrantType#PASSWORD resource owner password credentials} or 303 * {@link GrantType#CLIENT_CREDENTIALS client credentials grant}. 304 * 305 * @return The access scope, {@code null} if not specified. 306 */ 307 public Scope getScope() { 308 309 return scope; 310 } 311 312 313 @Override 314 public HTTPRequest toHTTPRequest(final URL url) 315 throws SerializeException { 316 317 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url); 318 httpRequest.setContentType(CommonContentTypes.APPLICATION_URLENCODED); 319 320 Map<String,String> params = new LinkedHashMap<String,String>(); 321 322 params.put("grant_type", getGrantType().toString()); 323 324 if (getGrantType().equals(GrantType.AUTHORIZATION_CODE)) { 325 326 params.put("code", code.toString()); 327 328 if (redirectURI != null) 329 params.put("redirect_uri", redirectURI.toString()); 330 331 if (clientID != null) 332 params.put("client_id", clientID.getValue()); 333 334 } else if (getGrantType().equals(GrantType.PASSWORD)) { 335 336 params.put("username", username); 337 338 params.put("password", password); 339 340 if (scope != null) 341 params.put("scope", scope.toString()); 342 343 } else if (getGrantType().equals(GrantType.CLIENT_CREDENTIALS)) { 344 345 if (scope != null) 346 params.put("scope", scope.toString()); 347 348 } else { 349 350 throw new SerializeException("Unsupported grant type: " + getGrantType()); 351 } 352 353 httpRequest.setQuery(URLUtils.serializeParameters(params)); 354 355 if (getClientAuthentication() != null) 356 getClientAuthentication().applyTo(httpRequest); 357 358 return httpRequest; 359 } 360 361 362 /** 363 * Parses the specified HTTP request for an access token request. 364 * 365 * @param httpRequest The HTTP request. Must not be {@code null}. 366 * 367 * @return The access token request. 368 * 369 * @throws ParseException If the HTTP request couldn't be parsed to an 370 * access token request. 371 */ 372 public static AccessTokenRequest parse(final HTTPRequest httpRequest) 373 throws ParseException { 374 375 // Only HTTP POST accepted 376 httpRequest.ensureMethod(HTTPRequest.Method.POST); 377 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 378 379 // No fragment! 380 // May use query component! 381 Map<String,String> params = httpRequest.getQueryParameters(); 382 383 384 // Parse grant type 385 String grantTypeString = params.get("grant_type"); 386 387 if (grantTypeString == null) 388 throw new ParseException("Missing \"grant_type\" parameter"); 389 390 GrantType grantType = new GrantType(grantTypeString); 391 392 if (grantType.equals(GrantType.AUTHORIZATION_CODE)) { 393 394 // Parse authorisation code 395 String codeString = params.get("code"); 396 397 if (codeString == null) 398 throw new ParseException("Missing \"code\" parameter"); 399 400 AuthorizationCode code = new AuthorizationCode(codeString); 401 402 403 // Parse redirect URI 404 String redirectURIString = params.get("redirect_uri"); 405 406 URL redirectURI = null; 407 408 if (redirectURIString != null) { 409 410 try { 411 redirectURI = new URL(redirectURIString); 412 413 } catch (MalformedURLException e) { 414 415 throw new ParseException("Invalid \"redirect_uri\" parameter: " + e.getMessage(), e); 416 } 417 } 418 419 420 // Parse client ID 421 String clientIDString = params.get("client_id"); 422 423 ClientID clientID = null; 424 425 if (clientIDString != null) 426 clientID = new ClientID(clientIDString); 427 428 // Parse client authentication 429 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 430 431 if (clientAuth != null) { 432 433 // Access token request with client authentication 434 return new AccessTokenRequest(code, redirectURI, clientAuth); 435 } 436 else { 437 if (clientID == null) 438 throw new ParseException("Missing \"client_id\" parameter"); 439 440 // Access token request with no client authentication 441 return new AccessTokenRequest(code, redirectURI, clientID); 442 } 443 } 444 else if (grantType.equals(GrantType.PASSWORD)) { 445 446 String username = params.get("username"); 447 448 if (username == null) 449 throw new ParseException("Missing \"username\" parameter"); 450 451 String password = params.get("password"); 452 453 if (password == null) 454 throw new ParseException("Missing \"password\" parameter"); 455 456 Scope scope = Scope.parse(params.get("scope")); 457 458 return new AccessTokenRequest(username, password, scope); 459 } 460 else if (grantType.equals(GrantType.CLIENT_CREDENTIALS)) { 461 462 Scope scope = Scope.parse(params.get("scope")); 463 464 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 465 466 if (clientAuth == null) 467 throw new ParseException("Missing client authentication"); 468 469 return new AccessTokenRequest(scope, clientAuth); 470 471 } 472 else { 473 throw new ParseException("Unsupported grant type: " + grantType); 474 } 475 } 476 }