001package com.nimbusds.openid.connect.sdk.claims; 002 003 004import java.util.*; 005 006import net.minidev.json.JSONObject; 007 008import com.nimbusds.jose.jwk.JWK; 009import com.nimbusds.jwt.JWTClaimsSet; 010 011import com.nimbusds.oauth2.sdk.ParseException; 012import com.nimbusds.oauth2.sdk.ResponseType; 013import com.nimbusds.oauth2.sdk.id.Audience; 014import com.nimbusds.oauth2.sdk.id.Issuer; 015import com.nimbusds.oauth2.sdk.id.Subject; 016import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 017 018import com.nimbusds.openid.connect.sdk.Nonce; 019 020 021/** 022 * ID token claims set, serialisable to a JSON object. 023 * 024 * <p>Example ID token claims set: 025 * 026 * <pre> 027 * { 028 * "iss" : "https://server.example.com", 029 * "sub" : "24400320", 030 * "aud" : "s6BhdRkqt3", 031 * "nonce" : "n-0S6_WzA2Mj", 032 * "exp" : 1311281970, 033 * "iat" : 1311280970, 034 * "auth_time" : 1311280969, 035 * "acr" : "urn:mace:incommon:iap:silver", 036 * "at_hash" : "MTIzNDU2Nzg5MDEyMzQ1Ng" 037 * } 038 * </pre> 039 * 040 * <p>Related specifications: 041 * 042 * <ul> 043 * <li>OpenID Connect Messages 1.0, section 2.1.2.1. 044 * </ul> 045 * 046 * @author Vladimir Dzhuvinov 047 */ 048public class IDTokenClaimsSet extends ClaimsSet { 049 050 051 /** 052 * The issuer claim name. 053 */ 054 public static final String ISS_CLAIM_NAME = "iss"; 055 056 057 /** 058 * The subject claim name. 059 */ 060 public static final String SUB_CLAIM_NAME = "sub"; 061 062 063 /** 064 * The audience claim name. 065 */ 066 public static final String AUD_CLAIM_NAME = "aud"; 067 068 069 /** 070 * The expiration time claim name. 071 */ 072 public static final String EXP_CLAIM_NAME = "exp"; 073 074 075 /** 076 * The issue time claim name. 077 */ 078 public static final String IAT_CLAIM_NAME = "iat"; 079 080 081 /** 082 * The subject authentication time claim name. 083 */ 084 public static final String AUTH_TIME_CLAIM_NAME = "auth_time"; 085 086 087 /** 088 * The nonce claim name. 089 */ 090 public static final String NONCE_CLAIM_NAME = "nonce"; 091 092 093 /** 094 * The access token hash claim name. 095 */ 096 public static final String AT_HASH_CLAIM_NAME = "at_hash"; 097 098 099 /** 100 * The authorisation code hash claim name. 101 */ 102 public static final String C_HASH_CLAIM_NAME = "c_hash"; 103 104 105 /** 106 * The ACR claim name. 107 */ 108 public static final String ACR_CLAIM_NAME = "acr"; 109 110 111 /** 112 * The AMRs claim name. 113 */ 114 public static final String AMR_CLAIM_NAME = "amr"; 115 116 117 /** 118 * The authorised party claim name. 119 */ 120 public static final String AZP_CLAIM_NAME = "azp"; 121 122 123 /** 124 * The subject JWK claim name. 125 */ 126 public static final String SUB_JWK_CLAIM_NAME = "sub_jwk"; 127 128 129 /** 130 * The names of the standard top-level ID token claims. 131 */ 132 private static final Set<String> stdClaimNames = new LinkedHashSet<String>(); 133 134 135 static { 136 stdClaimNames.add(ISS_CLAIM_NAME); 137 stdClaimNames.add(SUB_CLAIM_NAME); 138 stdClaimNames.add(AUD_CLAIM_NAME); 139 stdClaimNames.add(EXP_CLAIM_NAME); 140 stdClaimNames.add(IAT_CLAIM_NAME); 141 stdClaimNames.add(AUTH_TIME_CLAIM_NAME); 142 stdClaimNames.add(NONCE_CLAIM_NAME); 143 stdClaimNames.add(AT_HASH_CLAIM_NAME); 144 stdClaimNames.add(C_HASH_CLAIM_NAME); 145 stdClaimNames.add(ACR_CLAIM_NAME); 146 stdClaimNames.add(AMR_CLAIM_NAME); 147 stdClaimNames.add(AZP_CLAIM_NAME); 148 stdClaimNames.add(SUB_JWK_CLAIM_NAME); 149 } 150 151 152 /** 153 * Gets the names of the standard top-level ID token claims. 154 * 155 * @return The names of the standard top-level ID token claims 156 * (read-only set). 157 */ 158 public static Set<String> getStandardClaimNames() { 159 160 return Collections.unmodifiableSet(stdClaimNames); 161 } 162 163 164 /** 165 * Creates a new minimal ID token claims set. Note that the ID token 166 * may require additional claims to be present depending on the 167 * original OpenID Connect authorisation request. 168 * 169 * @param iss The issuer. Must not be {@code null}. 170 * @param sub The subject. Must not be {@code null}. 171 * @param aud The audience. Must not be {@code null}. 172 * @param exp The expiration time. Must not be {@code null}. 173 * @param iat The issue time. Must not be {@code null}. 174 */ 175 public IDTokenClaimsSet(final Issuer iss, 176 final Subject sub, 177 final List<Audience> aud, 178 final Date exp, 179 final Date iat) { 180 181 setClaim(ISS_CLAIM_NAME, iss.getValue()); 182 setClaim(SUB_CLAIM_NAME, sub.getValue()); 183 184 List<String> audList = new ArrayList<String>(aud.size()); 185 186 for (Audience a: aud) 187 audList.add(a.getValue()); 188 189 setClaim(AUD_CLAIM_NAME, audList); 190 191 setDateClaim(EXP_CLAIM_NAME, exp); 192 setDateClaim(IAT_CLAIM_NAME, iat); 193 } 194 195 196 /** 197 * Creates a new ID token claims set from the specified JSON object. 198 * 199 * @param jsonObject The JSON object. Must not be {@code null}. 200 * 201 * @throws IllegalArgumentException If the JSON object doesn't contain 202 * the minimally required issuer 203 * {@code iss}, subject {@code sub}, 204 * audience list {@code aud}, 205 * expiration date {@code exp} and 206 * issue date {@code iat} claims. 207 */ 208 public IDTokenClaimsSet(final JSONObject jsonObject) { 209 210 super(jsonObject); 211 212 if (getStringClaim(ISS_CLAIM_NAME) == null) 213 throw new IllegalArgumentException("Missing or invalid \"iss\" claim"); 214 215 if (getStringClaim(SUB_CLAIM_NAME) == null) 216 throw new IllegalArgumentException("Missing or invalid \"sub\" claim"); 217 218 if (getStringListClaim(AUD_CLAIM_NAME) == null) 219 throw new IllegalArgumentException("Missing or invalid \"aud\" claim"); 220 221 if (getDateClaim(EXP_CLAIM_NAME) == null) 222 throw new IllegalArgumentException("Missing or invalid \"exp\" claim"); 223 224 if (getDateClaim(IAT_CLAIM_NAME) == null) 225 throw new IllegalArgumentException("Missing or invalid \"iat\" claim"); 226 } 227 228 229 /** 230 * Creates a new ID token claims set from the specified JSON Web Token 231 * (JWT) claims set. 232 * 233 * @param jwtClaimsSet The JWT claims set. Must not be {@code null}. 234 * 235 * @throws IllegalArgumentException If the JWT claims set doesn't 236 * contain the minimally required 237 * issuer {@code iss}, subject 238 * {@code sub}, audience list 239 * {@code aud}, expiration date 240 * {@code exp} and issue date 241 * {@code iat} claims. 242 */ 243 public IDTokenClaimsSet(final JWTClaimsSet jwtClaimsSet) { 244 245 this(jwtClaimsSet.toJSONObject()); 246 } 247 248 249 /** 250 * Checks if this ID token claims set contains all required claims for 251 * the specified OpenID Connect response type. 252 * 253 * @param rt The OpenID Connect response type. Must not be 254 * {@code null}. 255 * 256 * @return {@code true} if the required claims are contained, else 257 * {@code false}. 258 */ 259 public boolean hasRequiredClaims(final ResponseType rt) { 260 261 if (rt.impliesImplicitFlow() && getNonce() == null) 262 return false; 263 264 if (rt.impliesImplicitFlow() && rt.contains(ResponseType.Value.TOKEN) && getAccessTokenHash() == null) 265 return false; 266 267 if (rt.impliesImplicitFlow() && rt.contains(ResponseType.Value.CODE) && getCodeHash() == null) 268 return false; 269 270 return true; 271 } 272 273 274 /** 275 * Gets the ID token issuer. Corresponds to the {@code iss} claim. 276 * 277 * @return The issuer. 278 */ 279 public Issuer getIssuer() { 280 281 return new Issuer(getStringClaim(ISS_CLAIM_NAME)); 282 } 283 284 285 /** 286 * Gets the ID token subject. Corresponds to the {@code sub} claim. 287 * 288 * @return The subject. 289 */ 290 public Subject getSubject() { 291 292 return new Subject(getStringClaim(SUB_CLAIM_NAME)); 293 } 294 295 296 /** 297 * Gets the ID token audience. Corresponds to the {@code aud} claim. 298 * 299 * @return The audience. 300 */ 301 public List<Audience> getAudience() { 302 303 List<String> rawList = getStringListClaim(AUD_CLAIM_NAME); 304 305 if (rawList == null || rawList.isEmpty()) 306 return null; 307 308 List<Audience> audList = new ArrayList<Audience>(rawList.size()); 309 310 for (String s: rawList) 311 audList.add(new Audience(s)); 312 313 return audList; 314 } 315 316 317 /** 318 * Gets the ID token expiration time. Corresponds to the {@code exp} 319 * claim. 320 * 321 * @return The expiration time. 322 */ 323 public Date getExpirationTime() { 324 325 return getDateClaim(EXP_CLAIM_NAME); 326 } 327 328 329 /** 330 * Gets the ID token issue time. Corresponds to the {@code iss} claim. 331 * 332 * @return The issue time. 333 */ 334 public Date getIssueTime() { 335 336 return getDateClaim(IAT_CLAIM_NAME); 337 } 338 339 340 /** 341 * Gets the subject authentication time. Corresponds to the 342 * {@code auth_time} claim. 343 * 344 * @return The authentication time, {@code null} if not specified. 345 */ 346 public Date getAuthenticationTime() { 347 348 return getDateClaim(AUTH_TIME_CLAIM_NAME); 349 } 350 351 352 /** 353 * Sets the subject authentication time. Corresponds to the 354 * {@code auth_time} claim. 355 * 356 * @param authTime The authentication time, {@code null} if not 357 * specified. 358 */ 359 public void setAuthenticationTime(final Date authTime) { 360 361 setDateClaim(AUTH_TIME_CLAIM_NAME, authTime); 362 } 363 364 365 /** 366 * Gets the ID token nonce. Corresponds to the {@code nonce} claim. 367 * 368 * @return The nonce, {@code null} if not specified. 369 */ 370 public Nonce getNonce() { 371 372 String value = getStringClaim(NONCE_CLAIM_NAME); 373 374 if (value == null) 375 return null; 376 377 return new Nonce(value); 378 } 379 380 381 /** 382 * Sets the ID token nonce. Corresponds to the {@code nonce} claim. 383 * 384 * @param nonce The nonce, {@code null} if not specified. 385 */ 386 public void setNonce(final Nonce nonce) { 387 388 if (nonce != null) 389 setClaim(NONCE_CLAIM_NAME, nonce.getValue()); 390 else 391 setClaim(NONCE_CLAIM_NAME, null); 392 } 393 394 395 /** 396 * Gets the access token hash. Corresponds to the {@code at_hash} 397 * claim. 398 * 399 * @return The access token hash, {@code null} if not specified. 400 */ 401 public AccessTokenHash getAccessTokenHash() { 402 403 String value = getStringClaim(AT_HASH_CLAIM_NAME); 404 405 if (value == null) 406 return null; 407 408 return new AccessTokenHash(value); 409 } 410 411 412 /** 413 * Sets the access token hash. Corresponds to the {@code at_hash} 414 * claim. 415 * 416 * @param atHash The access token hash, {@code null} if not specified. 417 */ 418 public void setAccessTokenHash(final AccessTokenHash atHash) { 419 420 if (atHash != null) 421 setClaim(AT_HASH_CLAIM_NAME, atHash.getValue()); 422 else 423 setClaim(AT_HASH_CLAIM_NAME, null); 424 } 425 426 427 /** 428 * Gets the authorisation code hash. Corresponds to the {@code c_hash} 429 * claim. 430 * 431 * @return The authorisation code hash, {@code null} if not specified. 432 */ 433 public CodeHash getCodeHash() { 434 435 String value = getStringClaim(C_HASH_CLAIM_NAME); 436 437 if (value == null) 438 return null; 439 440 return new CodeHash(value); 441 } 442 443 444 /** 445 * Sets the authorisation code hash. Corresponds to the {@code c_hash} 446 * claim. 447 * 448 * @param cHash The authorisation code hash, {@code null} if not 449 * specified. 450 */ 451 public void setCodeHash(final CodeHash cHash) { 452 453 if (cHash != null) 454 setClaim(C_HASH_CLAIM_NAME, cHash.getValue()); 455 else 456 setClaim(C_HASH_CLAIM_NAME, null); 457 } 458 459 460 /** 461 * Gets the Authentication Context Class Reference (ACR). Corresponds 462 * to the {@code acr} claim. 463 * 464 * @return The Authentication Context Class Reference (ACR), 465 * {@code null} if not specified. 466 */ 467 public ACR getACR() { 468 469 String value = getStringClaim(ACR_CLAIM_NAME); 470 471 if (value == null) 472 return null; 473 474 return new ACR(value); 475 } 476 477 478 /** 479 * Sets the Authentication Context Class Reference (ACR). Corresponds 480 * to the {@code acr} claim. 481 * 482 * @param acr The Authentication Context Class Reference (ACR), 483 * {@code null} if not specified. 484 */ 485 public void setACR(final ACR acr) { 486 487 if (acr != null) 488 setClaim(ACR_CLAIM_NAME, acr.getValue()); 489 else 490 setClaim(ACR_CLAIM_NAME, null); 491 } 492 493 494 /** 495 * Gets the Authentication Methods References (AMRs). Corresponds to 496 * the {@code amr} claim. 497 * 498 * @return The Authentication Methods Reference (AMR) list, 499 * {@code null} if not specified. 500 */ 501 public List<AMR> getAMR() { 502 503 List<String> rawList = getStringListClaim(AMR_CLAIM_NAME); 504 505 if (rawList == null || rawList.isEmpty()) 506 return null; 507 508 List<AMR> amrList = new ArrayList<AMR>(rawList.size()); 509 510 for (String s: rawList) 511 amrList.add(new AMR(s)); 512 513 return amrList; 514 } 515 516 517 /** 518 * Sets the Authentication Methods References (AMRs). Corresponds to 519 * the {@code amr} claim. 520 * 521 * @param amr The Authentication Methods Reference (AMR) list, 522 * {@code null} if not specified. 523 */ 524 public void setAMR(final List<AMR> amr) { 525 526 if (amr != null) { 527 528 List<String> amrList = new ArrayList<String>(amr.size()); 529 530 for (AMR a: amr) 531 amrList.add(a.getValue()); 532 533 setClaim(AMR_CLAIM_NAME, amrList); 534 535 } else { 536 setClaim(AMR_CLAIM_NAME, null); 537 } 538 } 539 540 541 /** 542 * Gets the authorised party for the ID token. Corresponds to the 543 * {@code azp} claim. 544 * 545 * @return The authorised party, {@code null} if not specified. 546 */ 547 public AuthorizedParty getAuthorizedParty() { 548 549 String value = getStringClaim(AZP_CLAIM_NAME); 550 551 if (value == null) 552 return null; 553 554 return new AuthorizedParty(value); 555 } 556 557 558 /** 559 * Sets the authorised party for the ID token. Corresponds to the 560 * {@code azp} claim. 561 * 562 * @param azp The authorised party, {@code null} if not specified. 563 */ 564 public void setAuthorizedParty(final AuthorizedParty azp) { 565 566 if (azp != null) 567 setClaim(AZP_CLAIM_NAME, azp.getValue()); 568 else 569 setClaim(AZP_CLAIM_NAME, null); 570 } 571 572 573 /** 574 * Gets the subject's JSON Web Key (JWK) for a self-issued OpenID 575 * Connect provider. Corresponds to the {@code sub_jwk} claim. 576 * 577 * @return The subject's JWK, {@code null} if not specified. 578 */ 579 public JWK getSubjectJWK() { 580 581 JSONObject jsonObject = getClaim(SUB_JWK_CLAIM_NAME, JSONObject.class); 582 583 if (jsonObject == null) 584 return null; 585 586 try { 587 return JWK.parse(jsonObject); 588 589 } catch (java.text.ParseException e) { 590 591 return null; 592 } 593 } 594 595 596 /** 597 * Sets the subject's JSON Web Key (JWK) for a self-issued OpenID 598 * Connect provider. Corresponds to the {@code sub_jwk} claim. 599 * 600 * @param subJWK The subject's JWK (must be public), {@code null} if 601 * not specified. 602 */ 603 public void setSubjectJWK(final JWK subJWK) { 604 605 if (subJWK != null) { 606 607 if (subJWK.isPrivate()) 608 throw new IllegalArgumentException("The subject's JSON Web Key (JWK) must be public"); 609 610 setClaim(SUB_JWK_CLAIM_NAME, subJWK.toJSONObject()); 611 612 } else { 613 setClaim(SUB_JWK_CLAIM_NAME, null); 614 } 615 } 616 617 618 /** 619 * Parses an ID token claims set from the specified JSON object string. 620 * 621 * @param json The JSON object string to parse. Must not be 622 * {@code null}. 623 * 624 * @return The ID token claims set. 625 * 626 * @throws ParseException If parsing failed. 627 */ 628 public static IDTokenClaimsSet parse(final String json) 629 throws ParseException { 630 631 JSONObject jsonObject = JSONObjectUtils.parseJSONObject(json); 632 633 try { 634 return new IDTokenClaimsSet(jsonObject); 635 636 } catch (IllegalArgumentException e) { 637 638 throw new ParseException(e.getMessage(), e); 639 } 640 } 641}