001package com.nimbusds.openid.connect.provider.spi.grants; 002 003 004import java.util.List; 005import java.util.Optional; 006 007import net.jcip.annotations.Immutable; 008import net.minidev.json.JSONObject; 009 010import com.nimbusds.oauth2.sdk.ParseException; 011import com.nimbusds.oauth2.sdk.id.Audience; 012import com.nimbusds.oauth2.sdk.id.Subject; 013import com.nimbusds.oauth2.sdk.token.TokenEncoding; 014import com.nimbusds.oauth2.sdk.util.CollectionUtils; 015import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 016import com.nimbusds.openid.connect.sdk.SubjectType; 017 018 019/** 020 * Access token specification. 021 */ 022@Immutable 023public class AccessTokenSpec extends TokenSpec { 024 025 026 /** 027 * The default access token specification. 028 * 029 * <ul> 030 * <li>No explicit token lifetime is specified, to let the 031 * Connect2id server apply the default configured lifetime for 032 * access tokens. 033 * <li>No explicit token audience is specified. 034 * <li>No subject in impersonation and delegation cases is 035 * specified. 036 * <li>The token is self-contained (JWT-encoded) and the optional 037 * encryption preference is not set. 038 * <li>The access token subject type is public. 039 * </ul> 040 */ 041 public static final AccessTokenSpec DEFAULT = new AccessTokenSpec(); 042 043 044 /** 045 * The access token encoding. 046 */ 047 private final TokenEncoding encoding; 048 049 050 /** 051 * The optional encryption preference for self-contained access 052 * tokens. 053 */ 054 private final Optional<Boolean> encryptSelfContained; 055 056 057 /** 058 * The access token subject type. 059 */ 060 private final SubjectType subjectType; 061 062 063 /** 064 * Creates a new default access token specification. No explicit 065 * token lifetime is specified (to let the Connect2id server apply the 066 * default configured lifetime for access tokens). No explicit token 067 * audience is specified. No subject in impersonation and delegation 068 * cases is specified. The token is self-contained (JWT-encoded) and 069 * the optional encryption preference is not set. The access token 070 * subject type is public. 071 */ 072 public AccessTokenSpec() { 073 074 this(0L, null, TokenEncoding.SELF_CONTAINED, null, Optional.empty(), SubjectType.PUBLIC); 075 } 076 077 078 /** 079 * Creates a new access token specification. No explicit token audience 080 * is specified. No subject in impersonation and delegation cases is 081 * specified. The access token subject type is public. 082 * 083 * @param lifetime The access token lifetime, in seconds, zero if not 084 * specified (to let the Connect2id server apply the 085 * default configured lifetime for access tokens). 086 * @param encoding The access token encoding. Must not be {@code null}. 087 * @param encrypt If {@code true} flags the access token for 088 * encryption. Applies to self-contained (JWT) access 089 * tokens only. 090 */ 091 @Deprecated 092 public AccessTokenSpec(final long lifetime, 093 final TokenEncoding encoding, 094 final boolean encrypt) { 095 096 this(lifetime, null, encoding, null, encrypt); 097 } 098 099 100 /** 101 * Creates a new access token specification. No subject in 102 * impersonation and delegation cases is specified. The access token 103 * subject type is public. 104 * 105 * @param lifetime The access token lifetime, in seconds, zero if not 106 * specified (to let the Connect2id server apply the 107 * default configured lifetime for access tokens). 108 * @param audList Explicit list of audiences for the access token, 109 * {@code null} if not specified. 110 * @param encoding The access token encoding. Must not be {@code null}. 111 * @param encrypt If {@code true} flags the access token for 112 * encryption. Applies to self-contained (JWT) access 113 * tokens only. 114 */ 115 @Deprecated 116 public AccessTokenSpec(final long lifetime, 117 final List<Audience> audList, 118 final TokenEncoding encoding, 119 final boolean encrypt) { 120 121 this(lifetime, audList, encoding, null, encrypt); 122 } 123 124 125 /** 126 * Creates a new access token specification. The access token subject 127 * type is public. 128 * 129 * @param lifetime The access token lifetime, in seconds, 130 * zero if not specified (to let the 131 * Connect2id server apply the default 132 * configured lifetime for access tokens). 133 * @param audList Explicit list of audiences for the access 134 * token, {@code null} if not specified. 135 * @param encoding The access token encoding. Must not be 136 * {@code null}. 137 * @param impersonatedSubject The subject in impersonation and 138 * delegation cases, {@code null} if not 139 * applicable. 140 * @param encrypt If {@code true} flags the access token 141 * for encryption. Applies to self-contained 142 * (JWT) access tokens only. 143 */ 144 @Deprecated 145 public AccessTokenSpec(final long lifetime, 146 final List<Audience> audList, 147 final TokenEncoding encoding, 148 final Subject impersonatedSubject, 149 final boolean encrypt) { 150 151 this(lifetime, audList, encoding, impersonatedSubject, Optional.of(encrypt), SubjectType.PUBLIC); 152 } 153 154 155 /** 156 * Creates a new access token specification. 157 * 158 * @param lifetime The access token lifetime, in seconds, 159 * zero if not specified (to let the 160 * Connect2id server apply the default 161 * configured lifetime for access tokens). 162 * @param audList Explicit list of audiences for the access 163 * token, {@code null} if not specified. 164 * @param encoding The access token encoding. Must not be 165 * {@code null}. 166 * @param impersonatedSubject The subject in impersonation and 167 * delegation cases, {@code null} if not 168 * applicable. 169 * @param encrypt If {@code true} flags the access token 170 * for encryption. Applies to self-contained 171 * (JWT) access tokens only. 172 * @param subjectType The access token subject type. 173 */ 174 @Deprecated 175 public AccessTokenSpec(final long lifetime, 176 final List<Audience> audList, 177 final TokenEncoding encoding, 178 final Subject impersonatedSubject, 179 final boolean encrypt, 180 final SubjectType subjectType) { 181 182 this(lifetime, audList, encoding, impersonatedSubject, Optional.of(encrypt), subjectType); 183 } 184 185 186 /** 187 * Creates a new access token specification. No subject in 188 * impersonation and delegation cases is specified. The access token 189 * subject type is public. 190 * 191 * @param lifetime The access token lifetime, in seconds, 192 * zero if not specified (to let the 193 * Connect2id server apply the default 194 * configured lifetime for access tokens). 195 * @param audList Explicit list of audiences for the 196 * access token, {@code null} if not 197 * specified. 198 * @param encoding The access token encoding. Must not be 199 * {@code null}. 200 * @param encryptSelfContained The optional encryption preference for 201 * self-contained (JWT) access tokens. 202 * Must not be {@code null}. 203 */ 204 public AccessTokenSpec(final long lifetime, 205 final List<Audience> audList, 206 final TokenEncoding encoding, 207 final Optional<Boolean> encryptSelfContained) { 208 209 this(lifetime, audList, encoding, null, encryptSelfContained, SubjectType.PUBLIC); 210 } 211 212 213 /** 214 * Creates a new access token specification. 215 * 216 * @param lifetime The access token lifetime, in seconds, 217 * zero if not specified (to let the 218 * Connect2id server apply the default 219 * configured lifetime for access tokens). 220 * @param audList Explicit list of audiences for the 221 * access token, {@code null} if not 222 * specified. 223 * @param encoding The access token encoding. Must not be 224 * {@code null}. 225 * @param impersonatedSubject The subject in impersonation and 226 * delegation cases, {@code null} if not 227 * applicable. 228 * @param encryptSelfContained The optional encryption preference for 229 * self-contained (JWT) access tokens. 230 * Must not be {@code null}. 231 * @param subjectType The access token subject type. 232 */ 233 public AccessTokenSpec(final long lifetime, 234 final List<Audience> audList, 235 final TokenEncoding encoding, 236 final Subject impersonatedSubject, 237 final Optional<Boolean> encryptSelfContained, 238 final SubjectType subjectType) { 239 240 super(lifetime, audList, impersonatedSubject); 241 242 if (encoding == null) { 243 throw new IllegalArgumentException("The access token encoding must not be null"); 244 } 245 246 this.encoding = encoding; 247 248 if (encryptSelfContained == null) { 249 throw new IllegalArgumentException("The optional encrypt self-contained must not be null"); 250 } 251 // Only JWT tokens may be encrypted 252 this.encryptSelfContained = TokenEncoding.SELF_CONTAINED.equals(encoding) ? encryptSelfContained : Optional.empty(); 253 254 if (subjectType == null) { 255 throw new IllegalArgumentException("The access token subject type must not be null"); 256 } 257 if (SubjectType.PAIRWISE.equals(subjectType) && CollectionUtils.isEmpty(audList)) { 258 throw new IllegalArgumentException("The pairwise token subject type requires an explicit token audience"); 259 } 260 this.subjectType = subjectType; 261 } 262 263 264 /** 265 * Returns the access token encoding. 266 * 267 * @return The access token encoding. 268 */ 269 public TokenEncoding getEncoding() { 270 271 return encoding; 272 } 273 274 275 /** 276 * Returns the access token encryption flag. 277 * 278 * @return If {@code true} the access token is flagged for encryption. 279 * Applies to self-contained access tokens only. 280 * 281 * @deprecated Use {@link #getEncryptSelfContained} instead. 282 */ 283 @Deprecated 284 public boolean encrypt() { 285 286 return encryptSelfContained.orElse(false); 287 } 288 289 290 /** 291 * Returns the optional encryption preference for self-contained (JWT) 292 * access tokens. 293 * 294 * @return The encryption preference. 295 */ 296 public Optional<Boolean> getEncryptSelfContained() { 297 298 return encryptSelfContained; 299 } 300 301 302 /** 303 * Returns the access token subject type. 304 * 305 * @return The subject type. 306 */ 307 public SubjectType getSubjectType() { 308 309 return subjectType; 310 } 311 312 313 @Override 314 public JSONObject toJSONObject() { 315 316 JSONObject o = super.toJSONObject(); 317 318 if (getLifetime() <= 0) { 319 // Implies not specified - remove 320 o.remove("lifetime"); 321 } 322 323 o.put("encoding", encoding.toString()); 324 325 if (encoding.equals(TokenEncoding.SELF_CONTAINED) && encryptSelfContained.isPresent()) { 326 o.put("encrypt", encryptSelfContained.get()); 327 } 328 329 o.put("sub_type", getSubjectType().toString().toUpperCase()); 330 331 return o; 332 } 333 334 335 /** 336 * Parses an access token specification from the specified JSON object. 337 * 338 * @param jsonObject The JSON object. Must not be {@code null}. 339 * 340 * @return The access token specification. 341 * 342 * @throws ParseException If parsing failed. 343 */ 344 public static AccessTokenSpec parse(final JSONObject jsonObject) 345 throws ParseException { 346 347 TokenSpec tokenSpec = TokenSpec.parse(jsonObject); 348 349 // Adjust lifetime value for not specified (0) 350 long lifetime = tokenSpec.getLifetime() > 0 ? tokenSpec.getLifetime() : 0; 351 352 TokenEncoding encoding = TokenEncoding.SELF_CONTAINED; 353 354 Optional<Boolean> encryptSelfContained = Optional.empty(); 355 356 if (jsonObject.get("encoding") != null) { 357 358 String c = JSONObjectUtils.getString(jsonObject, "encoding"); 359 360 try { 361 encoding = TokenEncoding.valueOf(c.toUpperCase()); 362 363 } catch (IllegalArgumentException e) { 364 365 throw new ParseException("Invalid access token encoding"); 366 } 367 } 368 369 if (encoding.equals(TokenEncoding.SELF_CONTAINED)) { 370 if (jsonObject.get("encrypt") != null) { 371 encryptSelfContained = Optional.of(JSONObjectUtils.getBoolean(jsonObject, "encrypt")); 372 } 373 } 374 375 SubjectType subjectType = JSONObjectUtils.getEnum(jsonObject, "sub_type", SubjectType.class, SubjectType.PUBLIC); 376 377 try { 378 return new AccessTokenSpec( 379 lifetime, 380 tokenSpec.getAudience(), 381 encoding, 382 tokenSpec.getImpersonatedSubject(), 383 encryptSelfContained, 384 subjectType 385 ); 386 } catch (IllegalArgumentException e) { 387 throw new ParseException(e.getMessage(), e); 388 } 389 } 390}