001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 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.jwt; 019 020 021import java.io.Serializable; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.text.ParseException; 025import java.util.*; 026 027import net.jcip.annotations.Immutable; 028 029import com.nimbusds.jwt.util.DateUtils; 030import com.nimbusds.jose.util.JSONArrayUtils; 031import com.nimbusds.jose.util.JSONObjectUtils; 032 033 034/** 035 * JSON Web Token (JWT) claims set. This class is immutable. 036 * 037 * <p>Supports all {@link #getRegisteredNames()} registered claims} of the JWT 038 * specification: 039 * 040 * <ul> 041 * <li>iss - Issuer 042 * <li>sub - Subject 043 * <li>aud - Audience 044 * <li>exp - Expiration Time 045 * <li>nbf - Not Before 046 * <li>iat - Issued At 047 * <li>jti - JWT ID 048 * </ul> 049 * 050 * <p>The set may also contain custom claims; these will be serialised and 051 * parsed along the registered ones. 052 * 053 * <p>Example JWT claims set: 054 * 055 * <pre> 056 * { 057 * "sub" : "joe", 058 * "exp" : 1300819380, 059 * "http://example.com/is_root" : true 060 * } 061 * </pre> 062 * 063 * <p>Example usage: 064 * 065 * <pre> 066 * JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() 067 * .subject("joe") 068 * .expirationTime(new Date(1300819380 * 1000l) 069 * .claim("http://example.com/is_root", true) 070 * .build(); 071 * </pre> 072 * 073 * @author Vladimir Dzhuvinov 074 * @author Justin Richer 075 * @version 2021-02-02 076 */ 077@Immutable 078public final class JWTClaimsSet implements Serializable { 079 080 081 private static final long serialVersionUID = 1L; 082 083 084 private static final String ISSUER_CLAIM = "iss"; 085 private static final String SUBJECT_CLAIM = "sub"; 086 private static final String AUDIENCE_CLAIM = "aud"; 087 private static final String EXPIRATION_TIME_CLAIM = "exp"; 088 private static final String NOT_BEFORE_CLAIM = "nbf"; 089 private static final String ISSUED_AT_CLAIM = "iat"; 090 private static final String JWT_ID_CLAIM = "jti"; 091 092 093 /** 094 * The registered claim names. 095 */ 096 private static final Set<String> REGISTERED_CLAIM_NAMES; 097 098 099 /* 100 * Initialises the registered claim name set. 101 */ 102 static { 103 Set<String> n = new HashSet<>(); 104 105 n.add(ISSUER_CLAIM); 106 n.add(SUBJECT_CLAIM); 107 n.add(AUDIENCE_CLAIM); 108 n.add(EXPIRATION_TIME_CLAIM); 109 n.add(NOT_BEFORE_CLAIM); 110 n.add(ISSUED_AT_CLAIM); 111 n.add(JWT_ID_CLAIM); 112 113 REGISTERED_CLAIM_NAMES = Collections.unmodifiableSet(n); 114 } 115 116 117 /** 118 * Builder for constructing JSON Web Token (JWT) claims sets. 119 * 120 * <p>Example usage: 121 * 122 * <pre> 123 * JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() 124 * .subject("joe") 125 * .expirationDate(new Date(1300819380 * 1000l) 126 * .claim("http://example.com/is_root", true) 127 * .build(); 128 * </pre> 129 */ 130 public static class Builder { 131 132 133 /** 134 * The claims. 135 */ 136 private final Map<String,Object> claims = new LinkedHashMap<>(); 137 138 139 /** 140 * Creates a new builder. 141 */ 142 public Builder() { 143 144 // Nothing to do 145 } 146 147 148 /** 149 * Creates a new builder with the claims from the specified 150 * set. 151 * 152 * @param jwtClaimsSet The JWT claims set to use. Must not be 153 * {@code null}. 154 */ 155 public Builder(final JWTClaimsSet jwtClaimsSet) { 156 157 claims.putAll(jwtClaimsSet.claims); 158 } 159 160 161 /** 162 * Sets the issuer ({@code iss}) claim. 163 * 164 * @param iss The issuer claim, {@code null} if not specified. 165 * 166 * @return This builder. 167 */ 168 public Builder issuer(final String iss) { 169 170 claims.put(ISSUER_CLAIM, iss); 171 return this; 172 } 173 174 175 /** 176 * Sets the subject ({@code sub}) claim. 177 * 178 * @param sub The subject claim, {@code null} if not specified. 179 * 180 * @return This builder. 181 */ 182 public Builder subject(final String sub) { 183 184 claims.put(SUBJECT_CLAIM, sub); 185 return this; 186 } 187 188 189 /** 190 * Sets the audience ({@code aud}) claim. 191 * 192 * @param aud The audience claim, {@code null} if not 193 * specified. 194 * 195 * @return This builder. 196 */ 197 public Builder audience(final List<String> aud) { 198 199 claims.put(AUDIENCE_CLAIM, aud); 200 return this; 201 } 202 203 204 /** 205 * Sets a single-valued audience ({@code aud}) claim. 206 * 207 * @param aud The audience claim, {@code null} if not 208 * specified. 209 * 210 * @return This builder. 211 */ 212 public Builder audience(final String aud) { 213 214 if (aud == null) { 215 claims.put(AUDIENCE_CLAIM, null); 216 } else { 217 claims.put(AUDIENCE_CLAIM, Collections.singletonList(aud)); 218 } 219 return this; 220 } 221 222 223 /** 224 * Sets the expiration time ({@code exp}) claim. 225 * 226 * @param exp The expiration time, {@code null} if not 227 * specified. 228 * 229 * @return This builder. 230 */ 231 public Builder expirationTime(final Date exp) { 232 233 claims.put(EXPIRATION_TIME_CLAIM, exp); 234 return this; 235 } 236 237 238 /** 239 * Sets the not-before ({@code nbf}) claim. 240 * 241 * @param nbf The not-before claim, {@code null} if not 242 * specified. 243 * 244 * @return This builder. 245 */ 246 public Builder notBeforeTime(final Date nbf) { 247 248 claims.put(NOT_BEFORE_CLAIM, nbf); 249 return this; 250 } 251 252 253 /** 254 * Sets the issued-at ({@code iat}) claim. 255 * 256 * @param iat The issued-at claim, {@code null} if not 257 * specified. 258 * 259 * @return This builder. 260 */ 261 public Builder issueTime(final Date iat) { 262 263 claims.put(ISSUED_AT_CLAIM, iat); 264 return this; 265 } 266 267 268 /** 269 * Sets the JWT ID ({@code jti}) claim. 270 * 271 * @param jti The JWT ID claim, {@code null} if not specified. 272 * 273 * @return This builder. 274 */ 275 public Builder jwtID(final String jti) { 276 277 claims.put(JWT_ID_CLAIM, jti); 278 return this; 279 } 280 281 282 /** 283 * Sets the specified claim (registered or custom). 284 * 285 * @param name The name of the claim to set. Must not be 286 * {@code null}. 287 * @param value The value of the claim to set, {@code null} if 288 * not specified. Should map to a JSON entity. 289 * 290 * @return This builder. 291 */ 292 public Builder claim(final String name, final Object value) { 293 294 claims.put(name, value); 295 return this; 296 } 297 298 299 /** 300 * Gets the claims (registered and custom). 301 * 302 * <p>Note that the registered claims Expiration-Time 303 * ({@code exp}), Not-Before-Time ({@code nbf}) and Issued-At 304 * ({@code iat}) will be returned as {@code java.util.Date} 305 * instances. 306 * 307 * @return The claims, as an unmodifiable map, empty map if 308 * none. 309 */ 310 public Map<String,Object> getClaims() { 311 312 return Collections.unmodifiableMap(claims); 313 } 314 315 316 /** 317 * Builds a new JWT claims set. 318 * 319 * @return The JWT claims set. 320 */ 321 public JWTClaimsSet build() { 322 323 return new JWTClaimsSet(claims); 324 } 325 } 326 327 328 /** 329 * The claims map. 330 */ 331 private final Map<String,Object> claims = new LinkedHashMap<>(); 332 333 334 /** 335 * Creates a new JWT claims set. 336 * 337 * @param claims The JWT claims set as a map. Must not be {@code null}. 338 */ 339 private JWTClaimsSet(final Map<String,Object> claims) { 340 341 this.claims.putAll(claims); 342 } 343 344 345 /** 346 * Gets the registered JWT claim names. 347 * 348 * @return The registered claim names, as a unmodifiable set. 349 */ 350 public static Set<String> getRegisteredNames() { 351 352 return REGISTERED_CLAIM_NAMES; 353 } 354 355 356 /** 357 * Gets the issuer ({@code iss}) claim. 358 * 359 * @return The issuer claim, {@code null} if not specified. 360 */ 361 public String getIssuer() { 362 363 try { 364 return getStringClaim(ISSUER_CLAIM); 365 } catch (ParseException e) { 366 return null; 367 } 368 } 369 370 371 /** 372 * Gets the subject ({@code sub}) claim. 373 * 374 * @return The subject claim, {@code null} if not specified. 375 */ 376 public String getSubject() { 377 378 try { 379 return getStringClaim(SUBJECT_CLAIM); 380 } catch (ParseException e) { 381 return null; 382 } 383 } 384 385 386 /** 387 * Gets the audience ({@code aud}) claim. 388 * 389 * @return The audience claim, empty list if not specified. 390 */ 391 public List<String> getAudience() { 392 393 Object audValue = getClaim(AUDIENCE_CLAIM); 394 395 if (audValue instanceof String) { 396 // Special case 397 return Collections.singletonList((String)audValue); 398 } 399 400 List<String> aud; 401 try { 402 aud = getStringListClaim(AUDIENCE_CLAIM); 403 } catch (ParseException e) { 404 return Collections.emptyList(); 405 } 406 return aud != null ? aud : Collections.<String>emptyList(); 407 } 408 409 410 /** 411 * Gets the expiration time ({@code exp}) claim. 412 * 413 * @return The expiration time, {@code null} if not specified. 414 */ 415 public Date getExpirationTime() { 416 417 try { 418 return getDateClaim(EXPIRATION_TIME_CLAIM); 419 } catch (ParseException e) { 420 return null; 421 } 422 } 423 424 425 /** 426 * Gets the not-before ({@code nbf}) claim. 427 * 428 * @return The not-before claim, {@code null} if not specified. 429 */ 430 public Date getNotBeforeTime() { 431 432 try { 433 return getDateClaim(NOT_BEFORE_CLAIM); 434 } catch (ParseException e) { 435 return null; 436 } 437 } 438 439 440 /** 441 * Gets the issued-at ({@code iat}) claim. 442 * 443 * @return The issued-at claim, {@code null} if not specified. 444 */ 445 public Date getIssueTime() { 446 447 try { 448 return getDateClaim(ISSUED_AT_CLAIM); 449 } catch (ParseException e) { 450 return null; 451 } 452 } 453 454 455 /** 456 * Gets the JWT ID ({@code jti}) claim. 457 * 458 * @return The JWT ID claim, {@code null} if not specified. 459 */ 460 public String getJWTID() { 461 462 try { 463 return getStringClaim(JWT_ID_CLAIM); 464 } catch (ParseException e) { 465 return null; 466 } 467 } 468 469 470 /** 471 * Gets the specified claim (registered or custom). 472 * 473 * @param name The name of the claim. Must not be {@code null}. 474 * 475 * @return The value of the claim, {@code null} if not specified. 476 */ 477 public Object getClaim(final String name) { 478 479 return claims.get(name); 480 } 481 482 483 /** 484 * Gets the specified claim (registered or custom) as 485 * {@link java.lang.String}. 486 * 487 * @param name The name of the claim. Must not be {@code null}. 488 * 489 * @return The value of the claim, {@code null} if not specified. 490 * 491 * @throws ParseException If the claim value is not of the required 492 * type. 493 */ 494 public String getStringClaim(final String name) 495 throws ParseException { 496 497 Object value = getClaim(name); 498 499 if (value == null || value instanceof String) { 500 return (String)value; 501 } else { 502 throw new ParseException("The \"" + name + "\" claim is not a String", 0); 503 } 504 } 505 506 507 /** 508 * Gets the specified claims (registered or custom) as a 509 * {@link java.lang.String} array. 510 * 511 * @param name The name of the claim. Must not be {@code null}. 512 * 513 * @return The value of the claim, {@code null} if not specified. 514 * 515 * @throws ParseException If the claim value is not of the required 516 * type. 517 */ 518 public String[] getStringArrayClaim(final String name) 519 throws ParseException { 520 521 Object value = getClaim(name); 522 523 if (value == null) { 524 return null; 525 } 526 527 List<?> list; 528 529 try { 530 list = (List<?>)getClaim(name); 531 532 } catch (ClassCastException e) { 533 throw new ParseException("The \"" + name + "\" claim is not a list / JSON array", 0); 534 } 535 536 String[] stringArray = new String[list.size()]; 537 538 for (int i=0; i < stringArray.length; i++) { 539 540 try { 541 stringArray[i] = (String)list.get(i); 542 } catch (ClassCastException e) { 543 throw new ParseException("The \"" + name + "\" claim is not a list / JSON array of strings", 0); 544 } 545 } 546 547 return stringArray; 548 } 549 550 551 /** 552 * Gets the specified claims (registered or custom) as a 553 * {@link java.util.List} list of strings. 554 * 555 * @param name The name of the claim. Must not be {@code null}. 556 * 557 * @return The value of the claim, {@code null} if not specified. 558 * 559 * @throws ParseException If the claim value is not of the required 560 * type. 561 */ 562 public List<String> getStringListClaim(final String name) 563 throws ParseException { 564 565 String[] stringArray = getStringArrayClaim(name); 566 567 if (stringArray == null) { 568 return null; 569 } 570 571 return Collections.unmodifiableList(Arrays.asList(stringArray)); 572 } 573 574 575 /** 576 * Gets the specified claim (registered or custom) as a 577 * {@link java.net.URI}. 578 * 579 * @param name The name of the claim. Must not be {@code null}. 580 * 581 * @return The value of the claim, {@code null} if not specified. 582 * 583 * @throws ParseException If the claim couldn't be parsed to a URI. 584 */ 585 public URI getURIClaim(final String name) 586 throws ParseException { 587 588 String uriString = getStringClaim(name); 589 590 if (uriString == null) { 591 return null; 592 } 593 594 try { 595 return new URI(uriString); 596 } catch (URISyntaxException e) { 597 throw new ParseException("The \"" + name + "\" claim is not a URI: " + e.getMessage(), 0); 598 } 599 } 600 601 602 /** 603 * Gets the specified claim (registered or custom) as 604 * {@link java.lang.Boolean}. 605 * 606 * @param name The name of the claim. Must not be {@code null}. 607 * 608 * @return The value of the claim, {@code null} if not specified. 609 * 610 * @throws ParseException If the claim value is not of the required 611 * type. 612 */ 613 public Boolean getBooleanClaim(final String name) 614 throws ParseException { 615 616 Object value = getClaim(name); 617 618 if (value == null || value instanceof Boolean) { 619 return (Boolean)value; 620 } else { 621 throw new ParseException("The \"" + name + "\" claim is not a Boolean", 0); 622 } 623 } 624 625 626 /** 627 * Gets the specified claim (registered or custom) as 628 * {@link java.lang.Integer}. 629 * 630 * @param name The name of the claim. Must not be {@code null}. 631 * 632 * @return The value of the claim, {@code null} if not specified. 633 * 634 * @throws ParseException If the claim value is not of the required 635 * type. 636 */ 637 public Integer getIntegerClaim(final String name) 638 throws ParseException { 639 640 Object value = getClaim(name); 641 642 if (value == null) { 643 return null; 644 } else if (value instanceof Number) { 645 return ((Number)value).intValue(); 646 } else { 647 throw new ParseException("The \"" + name + "\" claim is not an Integer", 0); 648 } 649 } 650 651 652 /** 653 * Gets the specified claim (registered or custom) as 654 * {@link java.lang.Long}. 655 * 656 * @param name The name of the claim. Must not be {@code null}. 657 * 658 * @return The value of the claim, {@code null} if not specified. 659 * 660 * @throws ParseException If the claim value is not of the required 661 * type. 662 */ 663 public Long getLongClaim(final String name) 664 throws ParseException { 665 666 Object value = getClaim(name); 667 668 if (value == null) { 669 return null; 670 } else if (value instanceof Number) { 671 return ((Number)value).longValue(); 672 } else { 673 throw new ParseException("The \"" + name + "\" claim is not a Number", 0); 674 } 675 } 676 677 678 /** 679 * Gets the specified claim (registered or custom) as 680 * {@link java.util.Date}. The claim may be represented by a Date 681 * object or a number of a seconds since the Unix epoch. 682 * 683 * @param name The name of the claim. Must not be {@code null}. 684 * 685 * @return The value of the claim, {@code null} if not specified. 686 * 687 * @throws ParseException If the claim value is not of the required 688 * type. 689 */ 690 public Date getDateClaim(final String name) 691 throws ParseException { 692 693 Object value = getClaim(name); 694 695 if (value == null) { 696 return null; 697 } else if (value instanceof Date) { 698 return (Date)value; 699 } else if (value instanceof Number) { 700 return DateUtils.fromSecondsSinceEpoch(((Number)value).longValue()); 701 } else { 702 throw new ParseException("The \"" + name + "\" claim is not a Date", 0); 703 } 704 } 705 706 707 /** 708 * Gets the specified claim (registered or custom) as 709 * {@link java.lang.Float}. 710 * 711 * @param name The name of the claim. Must not be {@code null}. 712 * 713 * @return The value of the claim, {@code null} if not specified. 714 * 715 * @throws ParseException If the claim value is not of the required 716 * type. 717 */ 718 public Float getFloatClaim(final String name) 719 throws ParseException { 720 721 Object value = getClaim(name); 722 723 if (value == null) { 724 return null; 725 } else if (value instanceof Number) { 726 return ((Number)value).floatValue(); 727 } else { 728 throw new ParseException("The \"" + name + "\" claim is not a Float", 0); 729 } 730 } 731 732 733 /** 734 * Gets the specified claim (registered or custom) as 735 * {@link java.lang.Double}. 736 * 737 * @param name The name of the claim. Must not be {@code null}. 738 * 739 * @return The value of the claim, {@code null} if not specified. 740 * 741 * @throws ParseException If the claim value is not of the required 742 * type. 743 */ 744 public Double getDoubleClaim(final String name) 745 throws ParseException { 746 747 Object value = getClaim(name); 748 749 if (value == null) { 750 return null; 751 } else if (value instanceof Number) { 752 return ((Number)value).doubleValue(); 753 } else { 754 throw new ParseException("The \"" + name + "\" claim is not a Double", 0); 755 } 756 } 757 758 759 /** 760 * Gets the specified claim (registered or custom) as a JSON object. 761 * 762 * @param name The name of the claim. Must not be {@code null}. 763 * 764 * @return The value of the claim, {@code null} if not specified. 765 * 766 * @throws ParseException If the claim value is not of the required 767 * type. 768 */ 769 public Map<String, Object> getJSONObjectClaim(final String name) 770 throws ParseException { 771 772 Object value = getClaim(name); 773 774 if (value == null) { 775 return null; 776 } else if (value instanceof Map) { 777 Map<String, Object> jsonObject = JSONObjectUtils.newJSONObject(); 778 Map<?,?> map = (Map<?,?>)value; 779 for (Map.Entry<?,?> entry: map.entrySet()) { 780 if (entry.getKey() instanceof String) { 781 jsonObject.put((String)entry.getKey(), entry.getValue()); 782 } 783 } 784 return jsonObject; 785 } else { 786 throw new ParseException("The \"" + name + "\" claim is not a JSON object or Map", 0); 787 } 788 } 789 790 791 /** 792 * Gets the claims (registered and custom). 793 * 794 * <p>Note that the registered claims Expiration-Time ({@code exp}), 795 * Not-Before-Time ({@code nbf}) and Issued-At ({@code iat}) will be 796 * returned as {@code java.util.Date} instances. 797 * 798 * @return The claims, as an unmodifiable map, empty map if none. 799 */ 800 public Map<String,Object> getClaims() { 801 802 return Collections.unmodifiableMap(claims); 803 } 804 805 806 /** 807 * Returns the JSON object representation of this claims set. The 808 * claims are serialised according to their insertion order. Claims 809 * with {@code null} values are not output. 810 * 811 * @return The JSON object representation. 812 */ 813 public Map<String, Object> toJSONObject() { 814 815 return toJSONObject(false); 816 } 817 818 819 /** 820 * Returns the JSON object representation of this claims set. The 821 * claims are serialised according to their insertion order. 822 * 823 * @param includeClaimsWithNullValues If {@code true} claims with 824 * {@code null} values will also be 825 * output. 826 * 827 * @return The JSON object representation. 828 */ 829 public Map<String, Object> toJSONObject(final boolean includeClaimsWithNullValues) { 830 831 Map<String, Object> o = JSONObjectUtils.newJSONObject(); 832 833 for (Map.Entry<String,Object> claim: claims.entrySet()) { 834 835 if (claim.getValue() instanceof Date) { 836 837 // Transform dates to Unix timestamps 838 Date dateValue = (Date) claim.getValue(); 839 o.put(claim.getKey(), DateUtils.toSecondsSinceEpoch(dateValue)); 840 841 } else if (AUDIENCE_CLAIM.equals(claim.getKey())) { 842 843 // Serialise single audience list and string 844 List<String> audList = getAudience(); 845 846 if (audList != null && ! audList.isEmpty()) { 847 if (audList.size() == 1) { 848 o.put(AUDIENCE_CLAIM, audList.get(0)); 849 } else { 850 List<Object> audArray = JSONArrayUtils.newJSONArray(); 851 audArray.addAll(audList); 852 o.put(AUDIENCE_CLAIM, audArray); 853 } 854 } else if (includeClaimsWithNullValues) { 855 o.put(AUDIENCE_CLAIM, null); 856 } 857 858 } else if (claim.getValue() != null) { 859 o.put(claim.getKey(), claim.getValue()); 860 } else if (includeClaimsWithNullValues) { 861 o.put(claim.getKey(), null); 862 } 863 } 864 865 return o; 866 } 867 868 869 /** 870 * Returns a JSON object string representation of this claims set. The 871 * claims are serialised according to their insertion order. Claims 872 * with {@code null} values are not output. 873 * 874 * @return The JSON object string representation. 875 */ 876 @Override 877 public String toString() { 878 879 return JSONObjectUtils.toJSONString(toJSONObject()); 880 } 881 882 883 /** 884 * Returns a JSON object string representation of this claims set. The 885 * claims are serialised according to their insertion order. 886 * 887 * @param includeClaimsWithNullValues If {@code true} claims with 888 * {@code null} values will also be 889 * output. 890 * 891 * @return The JSON object string representation. 892 */ 893 public String toString(final boolean includeClaimsWithNullValues) { 894 895 return JSONObjectUtils.toJSONString(toJSONObject(includeClaimsWithNullValues)); 896 } 897 898 899 /** 900 * Returns a transformation of this JWT claims set. 901 * 902 * @param <T> Type of the result. 903 * @param transformer The JWT claims set transformer. Must not be 904 * {@code null}. 905 * 906 * @return The transformed JWT claims set. 907 */ 908 public <T> T toType(final JWTClaimsSetTransformer<T> transformer) { 909 910 return transformer.transform(this); 911 } 912 913 914 /** 915 * Parses a JSON Web Token (JWT) claims set from the specified JSON 916 * object representation. 917 * 918 * @param json The JSON object to parse. Must not be {@code null}. 919 * 920 * @return The JWT claims set. 921 * 922 * @throws ParseException If the specified JSON object doesn't 923 * represent a valid JWT claims set. 924 */ 925 public static JWTClaimsSet parse(final Map<String, Object> json) 926 throws ParseException { 927 928 JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(); 929 930 // Parse registered + custom params 931 for (final String name: json.keySet()) { 932 933 switch (name) { 934 case ISSUER_CLAIM: 935 builder.issuer(JSONObjectUtils.getString(json, ISSUER_CLAIM)); 936 break; 937 case SUBJECT_CLAIM: 938 builder.subject(JSONObjectUtils.getString(json, SUBJECT_CLAIM)); 939 break; 940 case AUDIENCE_CLAIM: 941 Object audValue = json.get(AUDIENCE_CLAIM); 942 if (audValue instanceof String) { 943 List<String> singleAud = new ArrayList<>(); 944 singleAud.add(JSONObjectUtils.getString(json, AUDIENCE_CLAIM)); 945 builder.audience(singleAud); 946 } else if (audValue instanceof List) { 947 builder.audience(JSONObjectUtils.getStringList(json, AUDIENCE_CLAIM)); 948 } else if (audValue == null) { 949 builder.audience((String) null); 950 } 951 break; 952 case EXPIRATION_TIME_CLAIM: 953 builder.expirationTime(new Date(JSONObjectUtils.getLong(json, EXPIRATION_TIME_CLAIM) * 1000)); 954 break; 955 case NOT_BEFORE_CLAIM: 956 builder.notBeforeTime(new Date(JSONObjectUtils.getLong(json, NOT_BEFORE_CLAIM) * 1000)); 957 break; 958 case ISSUED_AT_CLAIM: 959 builder.issueTime(new Date(JSONObjectUtils.getLong(json, ISSUED_AT_CLAIM) * 1000)); 960 break; 961 case JWT_ID_CLAIM: 962 builder.jwtID(JSONObjectUtils.getString(json, JWT_ID_CLAIM)); 963 break; 964 default: 965 builder.claim(name, json.get(name)); 966 break; 967 } 968 } 969 970 return builder.build(); 971 } 972 973 974 /** 975 * Parses a JSON Web Token (JWT) claims set from the specified JSON 976 * object string representation. 977 * 978 * @param s The JSON object string to parse. Must not be {@code null}. 979 * 980 * @return The JWT claims set. 981 * 982 * @throws ParseException If the specified JSON object string doesn't 983 * represent a valid JWT claims set. 984 */ 985 public static JWTClaimsSet parse(final String s) 986 throws ParseException { 987 988 return parse(JSONObjectUtils.parse(s)); 989 } 990 991 992 @Override 993 public boolean equals(Object o) { 994 if (this == o) return true; 995 if (!(o instanceof JWTClaimsSet)) return false; 996 JWTClaimsSet that = (JWTClaimsSet) o; 997 return Objects.equals(claims, that.claims); 998 } 999 1000 1001 @Override 1002 public int hashCode() { 1003 return Objects.hash(claims); 1004 } 1005}