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