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 */ 055 @Immutable 056 public final class AccessTokenRequest extends TokenRequest { 057 058 059 // Authorisation code grant 060 061 /** 062 * The authorisation code received from the authorisation server. 063 */ 064 private final AuthorizationCode code; 065 066 067 /** 068 * The conditionally required redirect URI in the initial authorisation 069 * request. 070 */ 071 private final URL redirectURI; 072 073 074 /** 075 * The conditionally required client ID. 076 */ 077 private final ClientID clientID; 078 079 080 // Password credentials grant 081 082 /** 083 * The username. 084 */ 085 private final String username; 086 087 088 /** 089 * The password. 090 */ 091 private final String password; 092 093 094 // For password + client credentials grant 095 096 /** 097 * The access scope. 098 */ 099 private final Scope scope; 100 101 102 /** 103 * Creates a new unauthenticated access token request, using an 104 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 105 * 106 * @param code The authorisation code received from the 107 * authorisation server. Must not be {@code null}. 108 * @param redirectURI The redirect URI, may be {@code null} if 109 * specified in the initial authorisation request. 110 * @param clientID The client identifier. Must not be {@code null}. 111 */ 112 public AccessTokenRequest(final AuthorizationCode code, 113 final URL redirectURI, 114 final ClientID clientID) { 115 116 super(GrantType.AUTHORIZATION_CODE, null); 117 118 if (code == null) 119 throw new IllegalArgumentException("The authorization code must not be null"); 120 121 this.code = code; 122 123 124 this.redirectURI = redirectURI; 125 126 127 if (clientID == null) 128 throw new IllegalArgumentException("The client ID must not be null"); 129 130 this.clientID = clientID; 131 132 133 username = null; 134 password = null; 135 scope = null; 136 } 137 138 139 /** 140 * Creates a new authenticated access token request, using an 141 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 142 * 143 * @param code The authorisation code received from the 144 * authorisation server. Must not be {@code null}. 145 * @param redirectURI The redirect URI, may be {@code null} if not 146 * specified in the initial authorisation request. 147 * @param clientAuth The client authentication. Must not be 148 * {@code null}. 149 */ 150 public AccessTokenRequest(final AuthorizationCode code, 151 final URL redirectURI, 152 final ClientAuthentication clientAuth) { 153 154 super(GrantType.AUTHORIZATION_CODE, clientAuth); 155 156 if (code == null) 157 throw new IllegalArgumentException("The authorization code must not be null"); 158 159 this.code = code; 160 161 this.redirectURI = redirectURI; 162 163 if (clientAuth == null) 164 throw new IllegalArgumentException("The client authentication must not be null"); 165 166 167 clientID = null; 168 username = null; 169 password = null; 170 scope = null; 171 } 172 173 174 /** 175 * Creates a new authenticated access token request, using a 176 * {@link GrantType#PASSWORD resource owner password credentials grant}. 177 * 178 * @param username The resource owner username. Must not be 179 * {@code null}. 180 * @param password The resource owner password. Must not be 181 * {@code null}. 182 * @param scope The scope of the access request, {@code null} if not 183 * specified. 184 */ 185 public AccessTokenRequest(final String username, 186 final String password, 187 final Scope scope) { 188 189 super(GrantType.PASSWORD, null); 190 191 if (username == null) 192 throw new IllegalArgumentException("The username must not be null"); 193 194 this.username = username; 195 196 197 if (password == null) 198 throw new IllegalArgumentException("The password must not be null"); 199 200 this.password = password; 201 202 this.scope = scope; 203 204 code = null; 205 redirectURI = null; 206 clientID = null; 207 } 208 209 210 /** 211 * Creates a new authenticated access token request, using a 212 * {@link GrantType#CLIENT_CREDENTIALS client credentials grant}. 213 * 214 * @param scope The scope of the access request, {@code null} if 215 * not specified. 216 * @param clientAuth The client authentication. Must not be 217 * {@code null}. 218 */ 219 public AccessTokenRequest(final Scope scope, 220 final ClientAuthentication clientAuth) { 221 222 super(GrantType.CLIENT_CREDENTIALS, null); 223 224 this.scope = scope; 225 226 if (clientAuth == null) 227 throw new IllegalArgumentException("The client authentication must not be null"); 228 229 code = null; 230 redirectURI = null; 231 clientID = null; 232 username = null; 233 password = null; 234 } 235 236 237 /** 238 * Gets the authorisation code. Applies to requests using an 239 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 240 * 241 * @return The authorisation code, {@code null} if not specified. 242 */ 243 public AuthorizationCode getAuthorizationCode() { 244 245 return code; 246 } 247 248 249 /** 250 * Gets the redirect URI. Applies to requests using an 251 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant} 252 * 253 * @return The redirect URI, {@code null} if not specified. 254 */ 255 public URL getRedirectURI() { 256 257 return redirectURI; 258 } 259 260 261 /** 262 * Gets the client identifier. Applies to requests using an 263 * {@link GrantType#AUTHORIZATION_CODE authorisation code grant}. 264 * 265 * @return The client identifier, {@code null} if not specified. 266 */ 267 public ClientID getClientID() { 268 269 return clientID; 270 } 271 272 273 /** 274 * Gets the resource owner username. Applies to requests using a 275 * {@link GrantType#PASSWORD resource owner password credentials 276 * grant}. 277 * 278 * @return The resource owner username, {@code null} if not specified. 279 */ 280 public String getUsername() { 281 282 return username; 283 } 284 285 286 /** 287 * Gets the resource owner password. Applies to requests using a 288 * {@link GrantType#PASSWORD resource owner password credentials 289 * grant}. 290 * 291 * @return The resource owner password, {@code null} if not specified. 292 */ 293 public String getPassword() { 294 295 return password; 296 } 297 298 299 /** 300 * Gets the access scope. Applies to requests using a 301 * {@link GrantType#PASSWORD resource owner password credentials} or 302 * {@link GrantType#CLIENT_CREDENTIALS client credentials grant}. 303 * 304 * @return The access scope, {@code null} if not specified. 305 */ 306 public Scope getScope() { 307 308 return scope; 309 } 310 311 312 @Override 313 public HTTPRequest toHTTPRequest(final URL url) 314 throws SerializeException { 315 316 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url); 317 httpRequest.setContentType(CommonContentTypes.APPLICATION_URLENCODED); 318 319 Map<String,String> params = new LinkedHashMap<String,String>(); 320 321 params.put("grant_type", getGrantType().toString()); 322 323 if (getGrantType().equals(GrantType.AUTHORIZATION_CODE)) { 324 325 params.put("code", code.toString()); 326 327 if (redirectURI != null) 328 params.put("redirect_uri", redirectURI.toString()); 329 330 if (clientID != null) 331 params.put("client_id", clientID.getValue()); 332 333 } else if (getGrantType().equals(GrantType.PASSWORD)) { 334 335 params.put("username", username); 336 337 params.put("password", password); 338 339 if (scope != null) 340 params.put("scope", scope.toString()); 341 342 } else if (getGrantType().equals(GrantType.CLIENT_CREDENTIALS)) { 343 344 if (scope != null) 345 params.put("scope", scope.toString()); 346 347 } else { 348 349 throw new SerializeException("Unsupported grant type: " + getGrantType()); 350 } 351 352 httpRequest.setQuery(URLUtils.serializeParameters(params)); 353 354 if (getClientAuthentication() != null) 355 getClientAuthentication().applyTo(httpRequest); 356 357 return httpRequest; 358 } 359 360 361 /** 362 * Parses the specified HTTP request for an access token request. 363 * 364 * @param httpRequest The HTTP request. Must not be {@code null}. 365 * 366 * @return The access token request. 367 * 368 * @throws ParseException If the HTTP request couldn't be parsed to an 369 * access token request. 370 */ 371 public static AccessTokenRequest parse(final HTTPRequest httpRequest) 372 throws ParseException { 373 374 // Only HTTP POST accepted 375 httpRequest.ensureMethod(HTTPRequest.Method.POST); 376 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 377 378 // No fragment! 379 // May use query component! 380 Map<String,String> params = httpRequest.getQueryParameters(); 381 382 383 // Parse grant type 384 String grantTypeString = params.get("grant_type"); 385 386 if (grantTypeString == null) 387 throw new ParseException("Missing \"grant_type\" parameter"); 388 389 GrantType grantType = new GrantType(grantTypeString); 390 391 if (grantType.equals(GrantType.AUTHORIZATION_CODE)) { 392 393 // Parse authorisation code 394 String codeString = params.get("code"); 395 396 if (codeString == null) 397 throw new ParseException("Missing \"code\" parameter"); 398 399 AuthorizationCode code = new AuthorizationCode(codeString); 400 401 402 // Parse redirect URI 403 String redirectURIString = params.get("redirect_uri"); 404 405 URL redirectURI = null; 406 407 if (redirectURIString != null) { 408 409 try { 410 redirectURI = new URL(redirectURIString); 411 412 } catch (MalformedURLException e) { 413 414 throw new ParseException("Invalid \"redirect_uri\" parameter: " + e.getMessage(), e); 415 } 416 } 417 418 419 // Parse client ID 420 String clientIDString = params.get("client_id"); 421 422 ClientID clientID = null; 423 424 if (clientIDString != null) 425 clientID = new ClientID(clientIDString); 426 427 // Parse client authentication 428 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 429 430 if (clientAuth != null) { 431 432 // Access token request with client authentication 433 return new AccessTokenRequest(code, redirectURI, clientAuth); 434 435 } else { 436 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 } else { 472 473 throw new ParseException("Unsupported grant type: " + grantType); 474 } 475 } 476 }