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