001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.oauth2.sdk.token; 019 020 021import com.nimbusds.common.contenttype.ContentType; 022import com.nimbusds.oauth2.sdk.ParseException; 023import com.nimbusds.oauth2.sdk.Scope; 024import com.nimbusds.oauth2.sdk.http.HTTPRequest; 025import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail; 026import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 027import net.minidev.json.JSONObject; 028 029import java.util.*; 030 031 032/** 033 * The base abstract class for access tokens. Concrete extending classes should 034 * be immutable. 035 * 036 * <p>Related specifications: 037 * 038 * <ul> 039 * <li>OAuth 2.0 (RFC 6749) 040 * <li>OAuth 2.0 Rich Authorization Requests (RFC 9396) 041 * <li>OAuth 2.0 Token Exchange (RFC 8693) 042 * </ul> 043 */ 044public abstract class AccessToken extends Token { 045 046 047 private static final long serialVersionUID = 2947643641344083799L; 048 049 050 /** 051 * The access token type. 052 */ 053 private final AccessTokenType type; 054 055 056 /** 057 * Optional lifetime, in seconds. 058 */ 059 private final long lifetime; 060 061 062 /** 063 * Optional scope. 064 */ 065 private final Scope scope; 066 067 068 /** 069 * Optional authorisation details. 070 */ 071 private final List<AuthorizationDetail> authorizationDetails; 072 073 074 /** 075 * Optional identifier URI for the token type, as defined in OAuth 2.0 076 * Token Exchange (RFC 8693). 077 */ 078 private final TokenTypeURI issuedTokenType; 079 080 081 /** 082 * Creates a new minimal access token with a randomly generated 256-bit 083 * (32-byte) value, Base64URL-encoded. The optional lifetime, scope and 084 * token type URI are left unspecified. 085 * 086 * @param type The access token type. Must not be {@code null}. 087 */ 088 public AccessToken(final AccessTokenType type) { 089 090 this(type, 32); 091 } 092 093 094 /** 095 * Creates a new minimal access token with a randomly generated value 096 * of the specified byte length, Base64URL-encoded. The optional 097 * lifetime, scope and token type URI are left unspecified. 098 * 099 * @param type The access token type. Must not be {@code null}. 100 * @param byteLength The byte length of the value to generate. Must be 101 * greater than one. 102 */ 103 public AccessToken(final AccessTokenType type, final int byteLength) { 104 105 this(type, byteLength, 0L, null); 106 } 107 108 109 /** 110 * Creates a new access token with a randomly generated 256-bit 111 * (32-byte) value, Base64URL-encoded. The optional token type URI is 112 * left unspecified. 113 * 114 * @param type The access token type. Must not be {@code null}. 115 * @param lifetime The lifetime in seconds, 0 if not specified. 116 * @param scope The scope, {@code null} if not specified. 117 */ 118 public AccessToken(final AccessTokenType type, 119 final long lifetime, 120 final Scope scope) { 121 122 this(type, 32, lifetime, scope); 123 } 124 125 126 /** 127 * Creates a new access token with a randomly generated value 128 * of the specified byte length, Base64URL-encoded. The optional token 129 * type URI is left unspecified. 130 * 131 * @param type The access token type. Must not be {@code null}. 132 * @param byteLength The byte length of the value to generate. Must be 133 * greater than one. 134 * @param lifetime The lifetime in seconds, 0 if not specified. 135 * @param scope The scope, {@code null} if not specified. 136 */ 137 public AccessToken(final AccessTokenType type, 138 final int byteLength, 139 final long lifetime, 140 final Scope scope) { 141 142 this(type, byteLength, lifetime, scope, null); 143 } 144 145 146 /** 147 * Creates a new access token with a randomly generated value 148 * of the specified byte length, Base64URL-encoded. 149 * 150 * @param type The access token type. Must not be 151 * {@code null}. 152 * @param byteLength The byte length of the value to generate. 153 * Must be greater than one. 154 * @param lifetime The lifetime in seconds, 0 if not specified. 155 * @param scope The scope, {@code null} if not specified. 156 * @param issuedTokenType The token type URI, {@code null} if not 157 * specified. 158 */ 159 public AccessToken(final AccessTokenType type, 160 final int byteLength, 161 final long lifetime, 162 final Scope scope, 163 final TokenTypeURI issuedTokenType) { 164 165 this(type, byteLength, lifetime, scope, null, issuedTokenType); 166 } 167 168 169 /** 170 * Creates a new access token with a randomly generated value 171 * of the specified byte length, Base64URL-encoded. 172 * 173 * @param type The access token type. Must not be 174 * {@code null}. 175 * @param byteLength The byte length of the value to 176 * generate. Must be greater than one. 177 * @param lifetime The lifetime in seconds, 0 if not 178 * specified. 179 * @param scope The scope, {@code null} if not 180 * specified. 181 * @param authorizationDetails The authorisation details, {@code null} 182 * if not specified. 183 * @param issuedTokenType The token type URI, {@code null} if not 184 * specified. 185 */ 186 public AccessToken(final AccessTokenType type, 187 final int byteLength, 188 final long lifetime, 189 final Scope scope, 190 final List<AuthorizationDetail> authorizationDetails, 191 final TokenTypeURI issuedTokenType) { 192 193 super(byteLength); 194 195 this.type = Objects.requireNonNull(type); 196 this.lifetime = lifetime; 197 this.scope = scope; 198 this.authorizationDetails = authorizationDetails; 199 this.issuedTokenType = issuedTokenType; 200 } 201 202 203 /** 204 * Creates a new minimal access token with the specified value. The 205 * optional lifetime, scope and token type URI are left unspecified. 206 * 207 * @param type The access token type. Must not be {@code null}. 208 * @param value The access token value. Must not be {@code null} or 209 * empty string. 210 */ 211 public AccessToken(final AccessTokenType type, final String value) { 212 213 this(type, value, 0L, null); 214 } 215 216 217 /** 218 * Creates a new access token with the specified value. The optional 219 * token type URI is left unspecified. 220 * 221 * @param type The access token type. Must not be {@code null}. 222 * @param value The access token value. Must not be {@code null} or 223 * empty string. 224 * @param lifetime The lifetime in seconds, 0 if not specified. 225 * @param scope The scope, {@code null} if not specified. 226 */ 227 public AccessToken(final AccessTokenType type, 228 final String value, 229 final long lifetime, 230 final Scope scope) { 231 this(type, value, lifetime, scope, null); 232 } 233 234 235 /** 236 * Creates a new access token with the specified value. 237 * 238 * @param type The access token type. Must not be 239 * {@code null}. 240 * @param value The access token value. Must not be 241 * {@code null} or empty string. 242 * @param lifetime The lifetime in seconds, 0 if not specified. 243 * @param scope The scope, {@code null} if not specified. 244 * @param issuedTokenType The token type URI, {@code null} if not 245 * specified. 246 */ 247 public AccessToken(final AccessTokenType type, 248 final String value, 249 final long lifetime, 250 final Scope scope, 251 final TokenTypeURI issuedTokenType) { 252 253 this(type, value, lifetime, scope, null, issuedTokenType); 254 } 255 256 257 /** 258 * Creates a new access token with the specified value. 259 * 260 * @param type The access token type. Must not be 261 * {@code null}. 262 * @param value The access token value. Must not be 263 * {@code null} or empty string. 264 * @param lifetime The lifetime in seconds, 0 if not 265 * specified. 266 * @param scope The scope, {@code null} if not 267 * specified. 268 * @param authorizationDetails The authorisation details, {@code null} 269 * if not specified. 270 * @param issuedTokenType The token type URI, {@code null} if not 271 * specified. 272 */ 273 public AccessToken(final AccessTokenType type, 274 final String value, 275 final long lifetime, 276 final Scope scope, 277 final List<AuthorizationDetail> authorizationDetails, 278 final TokenTypeURI issuedTokenType) { 279 280 super(value); 281 this.type = Objects.requireNonNull(type); 282 this.lifetime = lifetime; 283 this.scope = scope; 284 this.authorizationDetails = authorizationDetails; 285 this.issuedTokenType = issuedTokenType; 286 } 287 288 289 /** 290 * Returns the access token type. 291 * 292 * @return The access token type. 293 */ 294 public AccessTokenType getType() { 295 296 return type; 297 } 298 299 300 /** 301 * Returns the lifetime of this access token. 302 * 303 * @return The lifetime in seconds, 0 if not specified. 304 */ 305 public long getLifetime() { 306 307 return lifetime; 308 } 309 310 311 /** 312 * Returns the scope of this access token. 313 * 314 * @return The scope, {@code null} if not specified. 315 */ 316 public Scope getScope() { 317 318 return scope; 319 } 320 321 322 /** 323 * Returns the authorisation details for this access token. 324 * 325 * @return The authorisation details, {@code null} if not specified. 326 */ 327 public List<AuthorizationDetail> getAuthorizationDetails() { 328 329 return authorizationDetails; 330 } 331 332 333 /** 334 * Returns the identifier URI for the type of this access token. Used 335 * in OAuth 2.0 Token Exchange (RFC 8693). 336 * 337 * @return The token type URI, {@code null} if not specified. 338 */ 339 public TokenTypeURI getIssuedTokenType() { 340 341 return issuedTokenType; 342 } 343 344 345 @Override 346 public Set<String> getParameterNames() { 347 348 Set<String> paramNames = new HashSet<>(getCustomParameters().keySet()); 349 paramNames.add("access_token"); 350 paramNames.add("token_type"); 351 352 if (getLifetime() > 0) 353 paramNames.add("expires_in"); 354 355 if (getScope() != null) 356 paramNames.add("scope"); 357 358 if (getAuthorizationDetails() != null) 359 paramNames.add("authorization_details"); 360 361 if (getIssuedTokenType() != null) { 362 paramNames.add("issued_token_type"); 363 } 364 365 return paramNames; 366 } 367 368 369 @Override 370 public JSONObject toJSONObject() { 371 372 JSONObject o = new JSONObject(); 373 374 o.putAll(getCustomParameters()); 375 376 o.put("access_token", getValue()); 377 o.put("token_type", type.toString()); 378 379 if (getLifetime() > 0) 380 o.put("expires_in", lifetime); 381 382 if (getScope() != null) 383 o.put("scope", scope.toString()); 384 385 if (getAuthorizationDetails() != null) 386 o.put("authorization_details", AuthorizationDetail.toJSONArray(getAuthorizationDetails())); 387 388 if (getIssuedTokenType() != null) { 389 o.put("issued_token_type", getIssuedTokenType().getURI().toString()); 390 } 391 392 return o; 393 } 394 395 396 @Override 397 public String toJSONString() { 398 399 return toJSONObject().toString(); 400 } 401 402 403 /** 404 * Returns the {@code Authorization} HTTP request header value for this 405 * access token. 406 * 407 * @return The {@code Authorization} header value. 408 */ 409 public abstract String toAuthorizationHeader(); 410 411 412 /** 413 * Parses an access token from a JSON object access token response. 414 * Only bearer and DPoP access tokens are supported. 415 * 416 * @param jsonObject The JSON object to parse. Must not be 417 * {@code null}. 418 * 419 * @return The access token. 420 * 421 * @throws ParseException If the JSON object couldn't be parsed to an 422 * access token. 423 */ 424 public static AccessToken parse(final JSONObject jsonObject) 425 throws ParseException { 426 427 AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getNonBlankString(jsonObject, "token_type")); 428 429 if (AccessTokenType.BEARER.equals(tokenType)) { 430 return BearerAccessToken.parse(jsonObject); 431 } else if (AccessTokenType.DPOP.equals(tokenType)) { 432 return DPoPAccessToken.parse(jsonObject); 433 } else if (AccessTokenType.N_A.equals(tokenType)) { 434 return NAAccessToken.parse(jsonObject); 435 } else { 436 throw new ParseException("Unsupported token_type: " + tokenType); 437 } 438 } 439 440 441 /** 442 * Parses an {@code Authorization} HTTP request header value for an 443 * access token. Only bearer access token are supported. 444 * 445 * @param header The {@code Authorization} header value to parse. Must 446 * not be {@code null}. 447 * 448 * @return The access token. 449 * 450 * @throws ParseException If the {@code Authorization} header value 451 * couldn't be parsed to an access token. 452 * 453 * @see #parse(String, AccessTokenType) 454 */ 455 @Deprecated 456 public static AccessToken parse(final String header) 457 throws ParseException { 458 459 return BearerAccessToken.parse(header); 460 } 461 462 463 /** 464 * Parses an {@code Authorization} HTTP request header value for an 465 * access token. Only bearer and DPoP access token are supported. 466 * 467 * @param header The {@code Authorization} header value to 468 * parse. Must not be {@code null}. 469 * @param preferredType The preferred (primary) access token type. 470 * Must be either {@link AccessTokenType#BEARER} 471 * or {@link AccessTokenType#DPOP} and not 472 * {@code null}. 473 * 474 * @return The access token. 475 * 476 * @throws ParseException If the {@code Authorization} header value 477 * couldn't be parsed to an access token. 478 */ 479 public static AccessToken parse(final String header, 480 final AccessTokenType preferredType) 481 throws ParseException { 482 483 if (! AccessTokenType.BEARER.equals(preferredType) && ! AccessTokenType.DPOP.equals(preferredType)) { 484 throw new IllegalArgumentException("Unsupported Authorization scheme: " + preferredType); 485 } 486 487 if (header != null && header.startsWith(AccessTokenType.BEARER.getValue()) || AccessTokenType.BEARER.equals(preferredType)) { 488 return BearerAccessToken.parse(header); 489 } else { 490 return DPoPAccessToken.parse(header); 491 } 492 } 493 494 495 /** 496 * Parses an HTTP request header value for an access token. 497 * 498 * @param request The HTTP request to parse. Must not be {@code null}. 499 * 500 * @return The access token. 501 * 502 * @throws ParseException If an access token wasn't found in the HTTP 503 * request. 504 */ 505 public static AccessToken parse(final HTTPRequest request) 506 throws ParseException { 507 508 if (request.getAuthorization() != null) { 509 510 AccessTokenType tokenType = AccessTokenUtils.determineAccessTokenTypeFromAuthorizationHeader(request.getAuthorization()); 511 512 if (AccessTokenType.BEARER.equals(tokenType)) { 513 return BearerAccessToken.parse(request.getAuthorization()); 514 } 515 516 if (AccessTokenType.DPOP.equals(tokenType)) { 517 return DPoPAccessToken.parse(request.getAuthorization()); 518 } 519 520 throw new ParseException("Couldn't determine access token type from Authorization header"); 521 } 522 523 // Try alternative token locations 524 Map<String, List<String>> params; 525 if (ContentType.APPLICATION_URLENCODED.matches(request.getEntityContentType())) { 526 // Form parameters 527 params = request.getBodyAsFormParameters(); 528 } else { 529 // Query string parameters 530 params = request.getQueryStringParameters(); 531 } 532 533 return new TypelessAccessToken(AccessTokenUtils.parseValueFromQueryParameters(params)); 534 } 535}