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.Set; 023 024import net.minidev.json.JSONObject; 025 026import com.nimbusds.oauth2.sdk.ParseException; 027import com.nimbusds.oauth2.sdk.Scope; 028import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 029 030 031/** 032 * The base abstract class for access tokens. Concrete extending classes should 033 * be immutable. 034 * 035 * <p>Related specifications: 036 * 037 * <ul> 038 * <li>OAuth 2.0 (RFC 6749), sections 1.4 and 5.1. 039 * </ul> 040 */ 041public abstract class AccessToken extends Token { 042 043 044 private static final long serialVersionUID = 2947643641344083799L; 045 046 047 /** 048 * The access token type. 049 */ 050 private final AccessTokenType type; 051 052 053 /** 054 * Optional lifetime, in seconds. 055 */ 056 private final long lifetime; 057 058 059 /** 060 * Optional scope. 061 */ 062 private final Scope scope; 063 064 065 /** 066 * Creates a new minimal access token with a randomly generated 256-bit 067 * (32-byte) value, Base64URL-encoded. The optional lifetime and scope 068 * are left undefined. 069 * 070 * @param type The access token type. Must not be {@code null}. 071 */ 072 public AccessToken(final AccessTokenType type) { 073 074 this(type, 32); 075 } 076 077 078 /** 079 * Creates a new minimal access token with a randomly generated value 080 * of the specified byte length, Base64URL-encoded. The optional 081 * lifetime and scope are left undefined. 082 * 083 * @param type The access token type. Must not be {@code null}. 084 * @param byteLength The byte length of the value to generate. Must be 085 * greater than one. 086 */ 087 public AccessToken(final AccessTokenType type, final int byteLength) { 088 089 this(type, byteLength, 0L, null); 090 } 091 092 093 /** 094 * Creates a new access token with a randomly generated 256-bit 095 * (32-byte) value, Base64URL-encoded. 096 * 097 * @param type The access token type. Must not be {@code null}. 098 * @param lifetime The lifetime in seconds, 0 if not specified. 099 * @param scope The scope, {@code null} if not specified. 100 */ 101 public AccessToken(final AccessTokenType type, 102 final long lifetime, 103 final Scope scope) { 104 105 this(type, 32, lifetime, scope); 106 } 107 108 109 /** 110 * Creates a new access token with a randomly generated value 111 * of the specified byte length, Base64URL-encoded, and optional 112 * lifetime and scope. 113 * 114 * @param type The access token type. Must not be {@code null}. 115 * @param byteLength The byte length of the value to generate. Must be 116 * greater than one. 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 int byteLength, 122 final long lifetime, 123 final Scope scope) { 124 125 super(byteLength); 126 127 if (type == null) 128 throw new IllegalArgumentException("The access token type must not be null"); 129 130 this.type = type; 131 132 this.lifetime = lifetime; 133 this.scope = scope; 134 } 135 136 137 /** 138 * Creates a new minimal access token with the specified value. The 139 * optional lifetime and scope are left undefined. 140 * 141 * @param type The access token type. Must not be {@code null}. 142 * @param value The access token value. Must not be {@code null} or 143 * empty string. 144 */ 145 public AccessToken(final AccessTokenType type, final String value) { 146 147 this(type, value, 0L, null); 148 } 149 150 151 /** 152 * Creates a new access token with the specified value and optional 153 * lifetime and scope. 154 * 155 * @param type The access token type. Must not be {@code null}. 156 * @param value The access token value. Must not be {@code null} or 157 * empty string. 158 * @param lifetime The lifetime in seconds, 0 if not specified. 159 * @param scope The scope, {@code null} if not specified. 160 */ 161 public AccessToken(final AccessTokenType type, 162 final String value, 163 final long lifetime, 164 final Scope scope) { 165 166 super(value); 167 168 if (type == null) 169 throw new IllegalArgumentException("The access token type must not be null"); 170 171 this.type = type; 172 173 this.lifetime = lifetime; 174 this.scope = scope; 175 } 176 177 178 /** 179 * Returns the access token type. 180 * 181 * @return The access token type. 182 */ 183 public AccessTokenType getType() { 184 185 return type; 186 } 187 188 189 /** 190 * Returns the lifetime of this access token. 191 * 192 * @return The lifetime in seconds, 0 if not specified. 193 */ 194 public long getLifetime() { 195 196 return lifetime; 197 } 198 199 200 /** 201 * Returns the scope of this access token. 202 * 203 * @return The scope, {@code null} if not specified. 204 */ 205 public Scope getScope() { 206 207 return scope; 208 } 209 210 211 @Override 212 public Set<String> getParameterNames() { 213 214 Set<String> paramNames = new HashSet<>(); 215 paramNames.add("access_token"); 216 paramNames.add("token_type"); 217 218 if (getLifetime() > 0) 219 paramNames.add("expires_in"); 220 221 if (getScope() != null) 222 paramNames.add("scope"); 223 224 return paramNames; 225 } 226 227 228 @Override 229 public JSONObject toJSONObject() { 230 231 JSONObject o = new JSONObject(); 232 233 o.put("access_token", getValue()); 234 o.put("token_type", type.toString()); 235 236 if (getLifetime() > 0) 237 o.put("expires_in", lifetime); 238 239 if (getScope() != null) 240 o.put("scope", scope.toString()); 241 242 return o; 243 } 244 245 246 @Override 247 public String toJSONString() { 248 249 return toJSONObject().toString(); 250 } 251 252 253 /** 254 * Returns the {@code Authorization} HTTP request header value for this 255 * access token. 256 * 257 * @return The {@code Authorization} header value. 258 */ 259 public abstract String toAuthorizationHeader(); 260 261 262 /** 263 * Parses an access token from a JSON object access token response. 264 * Only bearer and DPoP access tokens are supported. 265 * 266 * @param jsonObject The JSON object to parse. Must not be 267 * {@code null}. 268 * 269 * @return The access token. 270 * 271 * @throws ParseException If the JSON object couldn't be parsed to an 272 * access token. 273 */ 274 public static AccessToken parse(final JSONObject jsonObject) 275 throws ParseException { 276 277 AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type")); 278 279 if (AccessTokenType.BEARER.equals(tokenType)) { 280 return BearerAccessToken.parse(jsonObject); 281 } else if (AccessTokenType.DPOP.equals(tokenType)){ 282 return DPoPAccessToken.parse(jsonObject); 283 } else { 284 throw new ParseException("Unsupported token_type: " + tokenType); 285 } 286 } 287 288 289 /** 290 * Parses an {@code Authorization} HTTP request header value for an 291 * access token. Only bearer access token are supported. 292 * 293 * @param header The {@code Authorization} header value to parse. Must 294 * not be {@code null}. 295 * 296 * @return The access token. 297 * 298 * @throws ParseException If the {@code Authorization} header value 299 * couldn't be parsed to an access token. 300 * 301 * @see #parse(String, AccessTokenType) 302 */ 303 @Deprecated 304 public static AccessToken parse(final String header) 305 throws ParseException { 306 307 return BearerAccessToken.parse(header); 308 } 309 310 311 /** 312 * Parses an {@code Authorization} HTTP request header value for an 313 * access token. Only bearer and DPoP access token are supported. 314 * 315 * @param header The {@code Authorization} header value to 316 * parse. Must not be {@code null}. 317 * @param preferredType The preferred (primary) access token type. 318 * Must be either {@link AccessTokenType#BEARER} 319 * or {@link AccessTokenType#DPOP} and not 320 * {@code null}. 321 * 322 * @return The access token. 323 * 324 * @throws ParseException If the {@code Authorization} header value 325 * couldn't be parsed to an access token. 326 */ 327 public static AccessToken parse(final String header, 328 final AccessTokenType preferredType) 329 throws ParseException { 330 331 if (! AccessTokenType.BEARER.equals(preferredType) && ! AccessTokenType.DPOP.equals(preferredType)) { 332 throw new IllegalArgumentException("Unsupported Authorization scheme: " + preferredType); 333 } 334 335 if (header != null && header.startsWith(AccessTokenType.BEARER.getValue()) || AccessTokenType.BEARER.equals(preferredType)) { 336 return BearerAccessToken.parse(header); 337 } else { 338 return DPoPAccessToken.parse(header); 339 } 340 } 341}