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 com.nimbusds.jose.util.DateUtils; 028import com.nimbusds.jose.util.JSONObjectUtils; 029import net.jcip.annotations.Immutable; 030import net.minidev.json.JSONArray; 031import net.minidev.json.JSONObject; 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 2019-12-21 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 ? Collections.unmodifiableList(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 761 * {@link net.minidev.json.JSONObject}. 762 * 763 * @param name The name of the claim. Must not be {@code null}. 764 * 765 * @return The value of the claim, {@code null} if not specified. 766 * 767 * @throws ParseException If the claim value is not of the required 768 * type. 769 */ 770 public JSONObject getJSONObjectClaim(final String name) 771 throws ParseException { 772 773 Object value = getClaim(name); 774 775 if (value == null) { 776 return null; 777 } else if (value instanceof JSONObject) { 778 return (JSONObject)value; 779 } else if (value instanceof Map) { 780 JSONObject jsonObject = new JSONObject(); 781 Map<?,?> map = (Map<?,?>)value; 782 for (Map.Entry<?,?> entry: map.entrySet()) { 783 if (entry.getKey() instanceof String) { 784 jsonObject.put((String)entry.getKey(), entry.getValue()); 785 } 786 } 787 return jsonObject; 788 } else { 789 throw new ParseException("The \"" + name + "\" claim is not a JSON object or Map", 0); 790 } 791 } 792 793 794 /** 795 * Gets the claims (registered and custom). 796 * 797 * <p>Note that the registered claims Expiration-Time ({@code exp}), 798 * Not-Before-Time ({@code nbf}) and Issued-At ({@code iat}) will be 799 * returned as {@code java.util.Date} instances. 800 * 801 * @return The claims, as an unmodifiable map, empty map if none. 802 */ 803 public Map<String,Object> getClaims() { 804 805 return Collections.unmodifiableMap(claims); 806 } 807 808 809 /** 810 * Returns the JSON object representation of the claims set. The claims 811 * are serialised according to their insertion order. Claims with 812 * {@code null} values are not output. 813 * 814 * @return The JSON object representation. 815 */ 816 public JSONObject toJSONObject() { 817 818 return toJSONObject(false); 819 } 820 821 822 /** 823 * Returns the JSON object representation of the claims set. The claims 824 * are serialised according to their insertion order. 825 * 826 * @param includeClaimsWithNullValues If {@code true} claims with 827 * {@code null} values will also be 828 * output. 829 * 830 * @return The JSON object representation. 831 */ 832 public JSONObject toJSONObject(final boolean includeClaimsWithNullValues) { 833 834 JSONObject o = new JSONObject(); 835 836 for (Map.Entry<String,Object> claim: claims.entrySet()) { 837 838 if (claim.getValue() instanceof Date) { 839 840 // Transform dates to Unix timestamps 841 Date dateValue = (Date) claim.getValue(); 842 o.put(claim.getKey(), DateUtils.toSecondsSinceEpoch(dateValue)); 843 844 } else if (AUDIENCE_CLAIM.equals(claim.getKey())) { 845 846 // Serialise single audience list and string 847 List<String> audList = getAudience(); 848 849 if (audList != null && ! audList.isEmpty()) { 850 if (audList.size() == 1) { 851 o.put(AUDIENCE_CLAIM, audList.get(0)); 852 } else { 853 JSONArray audArray = new JSONArray(); 854 audArray.addAll(audList); 855 o.put(AUDIENCE_CLAIM, audArray); 856 } 857 } else if (includeClaimsWithNullValues) { 858 o.put(AUDIENCE_CLAIM, null); 859 } 860 861 } else if (claim.getValue() != null) { 862 o.put(claim.getKey(), claim.getValue()); 863 } else if (includeClaimsWithNullValues) { 864 o.put(claim.getKey(), null); 865 } 866 } 867 868 return o; 869 } 870 871 872 @Override 873 public String toString() { 874 875 return toJSONObject().toJSONString(); 876 } 877 878 879 /** 880 * Returns a transformation of this JWT claims set. 881 * 882 * @param <T> Type of the result. 883 * @param transformer The JWT claims set transformer. Must not be 884 * {@code null}. 885 * 886 * @return The transformed JWT claims set. 887 */ 888 public <T> T toType(final JWTClaimsSetTransformer<T> transformer) { 889 890 return transformer.transform(this); 891 } 892 893 894 /** 895 * Parses a JSON Web Token (JWT) claims set from the specified JSON 896 * object representation. 897 * 898 * @param json The JSON object to parse. Must not be {@code null}. 899 * 900 * @return The JWT claims set. 901 * 902 * @throws ParseException If the specified JSON object doesn't 903 * represent a valid JWT claims set. 904 */ 905 public static JWTClaimsSet parse(final JSONObject json) 906 throws ParseException { 907 908 JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(); 909 910 // Parse registered + custom params 911 for (final String name: json.keySet()) { 912 913 if (name.equals(ISSUER_CLAIM)) { 914 915 builder.issuer(JSONObjectUtils.getString(json, ISSUER_CLAIM)); 916 917 } else if (name.equals(SUBJECT_CLAIM)) { 918 919 builder.subject(JSONObjectUtils.getString(json, SUBJECT_CLAIM)); 920 921 } else if (name.equals(AUDIENCE_CLAIM)) { 922 923 Object audValue = json.get(AUDIENCE_CLAIM); 924 925 if (audValue instanceof String) { 926 List<String> singleAud = new ArrayList<>(); 927 singleAud.add(JSONObjectUtils.getString(json, AUDIENCE_CLAIM)); 928 builder.audience(singleAud); 929 } else if (audValue instanceof List) { 930 builder.audience(JSONObjectUtils.getStringList(json, AUDIENCE_CLAIM)); 931 } else if (audValue == null) { 932 builder.audience((String)null); 933 } 934 935 } else if (name.equals(EXPIRATION_TIME_CLAIM)) { 936 937 builder.expirationTime(new Date(JSONObjectUtils.getLong(json, EXPIRATION_TIME_CLAIM) * 1000)); 938 939 } else if (name.equals(NOT_BEFORE_CLAIM)) { 940 941 builder.notBeforeTime(new Date(JSONObjectUtils.getLong(json, NOT_BEFORE_CLAIM) * 1000)); 942 943 } else if (name.equals(ISSUED_AT_CLAIM)) { 944 945 builder.issueTime(new Date(JSONObjectUtils.getLong(json, ISSUED_AT_CLAIM) * 1000)); 946 947 } else if (name.equals(JWT_ID_CLAIM)) { 948 949 builder.jwtID(JSONObjectUtils.getString(json, JWT_ID_CLAIM)); 950 951 } else { 952 builder.claim(name, json.get(name)); 953 } 954 } 955 956 return builder.build(); 957 } 958 959 960 /** 961 * Parses a JSON Web Token (JWT) claims set from the specified JSON 962 * object string representation. 963 * 964 * @param s The JSON object string to parse. Must not be {@code null}. 965 * 966 * @return The JWT claims set. 967 * 968 * @throws ParseException If the specified JSON object string doesn't 969 * represent a valid JWT claims set. 970 */ 971 public static JWTClaimsSet parse(final String s) 972 throws ParseException { 973 974 return parse(JSONObjectUtils.parse(s)); 975 } 976 977 978 @Override 979 public boolean equals(Object o) { 980 if (this == o) return true; 981 if (!(o instanceof JWTClaimsSet)) return false; 982 JWTClaimsSet that = (JWTClaimsSet) o; 983 return Objects.equals(claims, that.claims); 984 } 985 986 987 @Override 988 public int hashCode() { 989 return Objects.hash(claims); 990 } 991}