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