001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 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.openid.connect.sdk.claims; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.util.*; 024import javax.mail.internet.InternetAddress; 025 026import com.nimbusds.jwt.JWT; 027import com.nimbusds.jwt.JWTClaimsSet; 028import com.nimbusds.jwt.JWTParser; 029import com.nimbusds.langtag.LangTag; 030import com.nimbusds.oauth2.sdk.ParseException; 031import com.nimbusds.oauth2.sdk.id.Subject; 032import com.nimbusds.oauth2.sdk.token.AccessToken; 033import com.nimbusds.oauth2.sdk.token.TypelessAccessToken; 034import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 035import net.minidev.json.JSONObject; 036 037 038/** 039 * UserInfo claims set, serialisable to a JSON object. 040 * 041 * <p>Supports normal, aggregated and distributed claims. 042 * 043 * <p>Example UserInfo claims set: 044 * 045 * <pre> 046 * { 047 * "sub" : "248289761001", 048 * "name" : "Jane Doe", 049 * "given_name" : "Jane", 050 * "family_name" : "Doe", 051 * "preferred_username" : "j.doe", 052 * "email" : "[email protected]", 053 * "picture" : "http://example.com/janedoe/me.jpg" 054 * } 055 * </pre> 056 * 057 * <p>Related specifications: 058 * 059 * <ul> 060 * <li>OpenID Connect Core 1.0, sections 5.1 and 5.6. 061 * </ul> 062 */ 063public class UserInfo extends ClaimsSet { 064 065 066 /** 067 * The subject claim name. 068 */ 069 public static final String SUB_CLAIM_NAME = "sub"; 070 071 072 /** 073 * The name claim name. 074 */ 075 public static final String NAME_CLAIM_NAME = "name"; 076 077 078 /** 079 * The given name claim name. 080 */ 081 public static final String GIVEN_NAME_CLAIM_NAME = "given_name"; 082 083 084 /** 085 * The family name claim name. 086 */ 087 public static final String FAMILY_NAME_CLAIM_NAME = "family_name"; 088 089 090 /** 091 * The middle name claim name. 092 */ 093 public static final String MIDDLE_NAME_CLAIM_NAME = "middle_name"; 094 095 096 /** 097 * The nickname claim name. 098 */ 099 public static final String NICKNAME_CLAIM_NAME = "nickname"; 100 101 102 /** 103 * The preferred username claim name. 104 */ 105 public static final String PREFERRED_USERNAME_CLAIM_NAME = "preferred_username"; 106 107 108 /** 109 * The profile claim name. 110 */ 111 public static final String PROFILE_CLAIM_NAME = "profile"; 112 113 114 /** 115 * The picture claim name. 116 */ 117 public static final String PICTURE_CLAIM_NAME = "picture"; 118 119 120 /** 121 * The website claim name. 122 */ 123 public static final String WEBSITE_CLAIM_NAME = "website"; 124 125 126 /** 127 * The email claim name. 128 */ 129 public static final String EMAIL_CLAIM_NAME = "email"; 130 131 132 /** 133 * The email verified claim name. 134 */ 135 public static final String EMAIL_VERIFIED_CLAIM_NAME = "email_verified"; 136 137 138 /** 139 * The gender claim name. 140 */ 141 public static final String GENDER_CLAIM_NAME = "gender"; 142 143 144 /** 145 * The birth date claim name. 146 */ 147 public static final String BIRTHDATE_CLAIM_NAME = "birthdate"; 148 149 150 /** 151 * The zoneinfo claim name. 152 */ 153 public static final String ZONEINFO_CLAIM_NAME = "zoneinfo"; 154 155 156 /** 157 * The locale claim name. 158 */ 159 public static final String LOCALE_CLAIM_NAME = "locale"; 160 161 162 /** 163 * The phone number claim name. 164 */ 165 public static final String PHONE_NUMBER_CLAIM_NAME = "phone_number"; 166 167 168 /** 169 * The phone number verified claim name. 170 */ 171 public static final String PHONE_NUMBER_VERIFIED_CLAIM_NAME = "phone_number_verified"; 172 173 174 /** 175 * The address claim name. 176 */ 177 public static final String ADDRESS_CLAIM_NAME = "address"; 178 179 180 /** 181 * The updated at claim name. 182 */ 183 public static final String UPDATED_AT_CLAIM_NAME = "updated_at"; 184 185 186 /** 187 * The names of the standard top-level UserInfo claims. 188 */ 189 private static final Set<String> stdClaimNames = new LinkedHashSet<>(); 190 191 192 static { 193 stdClaimNames.add(SUB_CLAIM_NAME); 194 stdClaimNames.add(NAME_CLAIM_NAME); 195 stdClaimNames.add(GIVEN_NAME_CLAIM_NAME); 196 stdClaimNames.add(FAMILY_NAME_CLAIM_NAME); 197 stdClaimNames.add(MIDDLE_NAME_CLAIM_NAME); 198 stdClaimNames.add(NICKNAME_CLAIM_NAME); 199 stdClaimNames.add(PREFERRED_USERNAME_CLAIM_NAME); 200 stdClaimNames.add(PROFILE_CLAIM_NAME); 201 stdClaimNames.add(PICTURE_CLAIM_NAME); 202 stdClaimNames.add(WEBSITE_CLAIM_NAME); 203 stdClaimNames.add(EMAIL_CLAIM_NAME); 204 stdClaimNames.add(EMAIL_VERIFIED_CLAIM_NAME); 205 stdClaimNames.add(GENDER_CLAIM_NAME); 206 stdClaimNames.add(BIRTHDATE_CLAIM_NAME); 207 stdClaimNames.add(ZONEINFO_CLAIM_NAME); 208 stdClaimNames.add(LOCALE_CLAIM_NAME); 209 stdClaimNames.add(PHONE_NUMBER_CLAIM_NAME); 210 stdClaimNames.add(PHONE_NUMBER_VERIFIED_CLAIM_NAME); 211 stdClaimNames.add(ADDRESS_CLAIM_NAME); 212 stdClaimNames.add(UPDATED_AT_CLAIM_NAME); 213 } 214 215 216 /** 217 * Gets the names of the standard top-level UserInfo claims. 218 * 219 * @return The names of the standard top-level UserInfo claims 220 * (read-only set). 221 */ 222 public static Set<String> getStandardClaimNames() { 223 224 return Collections.unmodifiableSet(stdClaimNames); 225 } 226 227 228 /** 229 * Creates a new minimal UserInfo claims set. 230 * 231 * @param sub The subject. Must not be {@code null}. 232 */ 233 public UserInfo(final Subject sub) { 234 235 setClaim(SUB_CLAIM_NAME, sub.getValue()); 236 } 237 238 239 /** 240 * Creates a new UserInfo claims set from the specified JSON object. 241 * 242 * @param jsonObject The JSON object. Must not be {@code null}. 243 * 244 * @throws IllegalArgumentException If the JSON object doesn't contain 245 * a subject {@code sub} string claim. 246 */ 247 public UserInfo(final JSONObject jsonObject) { 248 249 super(jsonObject); 250 251 if (getStringClaim(SUB_CLAIM_NAME) == null) 252 throw new IllegalArgumentException("Missing or invalid \"sub\" claim"); 253 } 254 255 256 /** 257 * Creates a new UserInfo claims set from the specified JSON Web Token 258 * (JWT) claims set. 259 * 260 * @param jwtClaimsSet The JWT claims set. Must not be {@code null}. 261 * 262 * @throws IllegalArgumentException If the JWT claims set doesn't 263 * contain a subject {@code sub} 264 * string claim. 265 */ 266 public UserInfo(final JWTClaimsSet jwtClaimsSet) { 267 268 this(jwtClaimsSet.toJSONObject()); 269 } 270 271 272 /** 273 * Puts all claims from the specified other UserInfo claims set. 274 * Aggregated and distributed claims are properly merged. 275 * 276 * @param other The other UserInfo. Must have the same 277 * {@link #getSubject subject}. Must not be {@code null}. 278 * 279 * @throws IllegalArgumentException If the other UserInfo claims set 280 * doesn't have an identical subject, 281 * or if the external claims source ID 282 * of the other UserInfo matches an 283 * existing source ID. 284 */ 285 public void putAll(final UserInfo other) { 286 287 Subject otherSubject = other.getSubject(); 288 289 if (otherSubject == null) 290 throw new IllegalArgumentException("The subject of the other UserInfo is missing"); 291 292 if (! otherSubject.equals(getSubject())) 293 throw new IllegalArgumentException("The subject of the other UserInfo must be identical"); 294 295 // Save present aggregated and distributed claims, to prevent 296 // overwrite by put to claims JSON object 297 Set<AggregatedClaims> savedAggregatedClaims = getAggregatedClaims(); 298 Set<DistributedClaims> savedDistributedClaims = getDistributedClaims(); 299 300 // Save other present aggregated and distributed claims 301 Set<AggregatedClaims> otherAggregatedClaims = other.getAggregatedClaims(); 302 Set<DistributedClaims> otherDistributedClaims = other.getDistributedClaims(); 303 304 // Ensure external source IDs don't conflict during merge 305 Set<String> externalSourceIDs = new HashSet<>(); 306 307 if (savedAggregatedClaims != null) { 308 for (AggregatedClaims ac: savedAggregatedClaims) { 309 externalSourceIDs.add(ac.getSourceID()); 310 } 311 } 312 313 if (savedDistributedClaims != null) { 314 for (DistributedClaims dc: savedDistributedClaims) { 315 externalSourceIDs.add(dc.getSourceID()); 316 } 317 } 318 319 if (otherAggregatedClaims != null) { 320 for (AggregatedClaims ac: otherAggregatedClaims) { 321 if (externalSourceIDs.contains(ac.getSourceID())) { 322 throw new IllegalArgumentException("Aggregated claims source ID conflict: " + ac.getSourceID()); 323 } 324 } 325 } 326 327 if (otherDistributedClaims != null) { 328 for (DistributedClaims dc: otherDistributedClaims) { 329 if (externalSourceIDs.contains(dc.getSourceID())) { 330 throw new IllegalArgumentException("Distributed claims source ID conflict: " + dc.getSourceID()); 331 } 332 } 333 } 334 335 putAll((ClaimsSet)other); 336 337 // Merge saved external claims, if any 338 if (savedAggregatedClaims != null) { 339 for (AggregatedClaims ac: savedAggregatedClaims) { 340 addAggregatedClaims(ac); 341 } 342 } 343 344 if (savedDistributedClaims != null) { 345 for (DistributedClaims dc: savedDistributedClaims) { 346 addDistributedClaims(dc); 347 } 348 } 349 } 350 351 352 /** 353 * Gets the UserInfo subject. Corresponds to the {@code sub} claim. 354 * 355 * @return The subject. 356 */ 357 public Subject getSubject() { 358 359 return new Subject(getStringClaim(SUB_CLAIM_NAME)); 360 } 361 362 363 /** 364 * Gets the full name. Corresponds to the {@code name} claim, with no 365 * language tag. 366 * 367 * @return The full name, {@code null} if not specified. 368 */ 369 public String getName() { 370 371 return getStringClaim(NAME_CLAIM_NAME); 372 } 373 374 375 /** 376 * Gets the full name. Corresponds to the {@code name} claim, with an 377 * optional language tag. 378 * 379 * @param langTag The language tag of the entry, {@code null} to get 380 * the non-tagged entry. 381 * 382 * @return The full name, {@code null} if not specified. 383 */ 384 public String getName(final LangTag langTag) { 385 386 return getStringClaim(NAME_CLAIM_NAME, langTag); 387 } 388 389 390 /** 391 * Gets the full name entries. Correspond to the {@code name} claim. 392 * 393 * @return The full name entries, empty map if none. 394 */ 395 public Map<LangTag,String> getNameEntries() { 396 397 return getLangTaggedClaim(NAME_CLAIM_NAME, String.class); 398 } 399 400 401 /** 402 * Sets the full name. Corresponds to the {@code name} claim, with no 403 * language tag. 404 * 405 * @param name The full name. If {@code null} the claim will be 406 * removed. 407 */ 408 public void setName(final String name) { 409 410 setClaim(NAME_CLAIM_NAME, name); 411 } 412 413 414 /** 415 * Sets the full name. Corresponds to the {@code name} claim, with an 416 * optional language tag. 417 * 418 * @param name The full name. If {@code null} the claim will be 419 * removed. 420 * @param langTag The language tag, {@code null} if not specified. 421 */ 422 public void setName(final String name, final LangTag langTag) { 423 424 setClaim(NAME_CLAIM_NAME, name, langTag); 425 } 426 427 428 /** 429 * Gets the given or first name. Corresponds to the {@code given_name} 430 * claim, with no language tag. 431 * 432 * @return The given or first name, {@code null} if not specified. 433 */ 434 public String getGivenName() { 435 436 return getStringClaim(GIVEN_NAME_CLAIM_NAME); 437 } 438 439 440 /** 441 * Gets the given or first name. Corresponds to the {@code given_name} 442 * claim, with an optional language tag. 443 * 444 * @param langTag The language tag of the entry, {@code null} to get 445 * the non-tagged entry. 446 * 447 * @return The given or first name, {@code null} if not specified. 448 */ 449 public String getGivenName(final LangTag langTag) { 450 451 return getStringClaim(GIVEN_NAME_CLAIM_NAME, langTag); 452 } 453 454 455 /** 456 * Gets the given or first name entries. Correspond to the 457 * {@code given_name} claim. 458 * 459 * @return The given or first name entries, empty map if none. 460 */ 461 public Map<LangTag,String> getGivenNameEntries() { 462 463 return getLangTaggedClaim(GIVEN_NAME_CLAIM_NAME, String.class); 464 } 465 466 467 /** 468 * Sets the given or first name. Corresponds to the {@code given_name} 469 * claim, with no language tag. 470 * 471 * @param givenName The given or first name. If {@code null} the claim 472 * will be removed. 473 */ 474 public void setGivenName(final String givenName) { 475 476 setClaim(GIVEN_NAME_CLAIM_NAME, givenName); 477 } 478 479 480 /** 481 * Sets the given or first name. Corresponds to the {@code given_name} 482 * claim, with an optional language tag. 483 * 484 * @param givenName The given or first full name. If {@code null} the 485 * claim will be removed. 486 * @param langTag The language tag, {@code null} if not specified. 487 */ 488 public void setGivenName(final String givenName, final LangTag langTag) { 489 490 setClaim(GIVEN_NAME_CLAIM_NAME, givenName, langTag); 491 } 492 493 494 /** 495 * Gets the surname or last name. Corresponds to the 496 * {@code family_name} claim, with no language tag. 497 * 498 * @return The surname or last name, {@code null} if not specified. 499 */ 500 public String getFamilyName() { 501 502 return getStringClaim(FAMILY_NAME_CLAIM_NAME); 503 } 504 505 506 /** 507 * Gets the surname or last name. Corresponds to the 508 * {@code family_name} claim, with an optional language tag. 509 * 510 * @param langTag The language tag of the entry, {@code null} to get 511 * the non-tagged entry. 512 * 513 * @return The surname or last name, {@code null} if not specified. 514 */ 515 public String getFamilyName(final LangTag langTag) { 516 517 return getStringClaim(FAMILY_NAME_CLAIM_NAME, langTag); 518 } 519 520 521 /** 522 * Gets the surname or last name entries. Correspond to the 523 * @code family_name} claim. 524 * 525 * @return The surname or last name entries, empty map if none. 526 */ 527 public Map<LangTag,String> getFamilyNameEntries() { 528 529 return getLangTaggedClaim(FAMILY_NAME_CLAIM_NAME, String.class); 530 } 531 532 533 /** 534 * Sets the surname or last name. Corresponds to the 535 * {@code family_name} claim, with no language tag. 536 * 537 * @param familyName The surname or last name. If {@code null} the 538 * claim will be removed. 539 */ 540 public void setFamilyName(final String familyName) { 541 542 setClaim(FAMILY_NAME_CLAIM_NAME, familyName); 543 } 544 545 546 /** 547 * Sets the surname or last name. Corresponds to the 548 * {@code family_name} claim, with an optional language tag. 549 * 550 * @param familyName The surname or last name. If {@code null} the 551 * claim will be removed. 552 * @param langTag The language tag, {@code null} if not specified. 553 */ 554 public void setFamilyName(final String familyName, final LangTag langTag) { 555 556 setClaim(FAMILY_NAME_CLAIM_NAME, familyName, langTag); 557 } 558 559 560 /** 561 * Gets the middle name. Corresponds to the {@code middle_name} claim, 562 * with no language tag. 563 * 564 * @return The middle name, {@code null} if not specified. 565 */ 566 public String getMiddleName() { 567 568 return getStringClaim(MIDDLE_NAME_CLAIM_NAME); 569 } 570 571 572 /** 573 * Gets the middle name. Corresponds to the {@code middle_name} claim, 574 * with an optional language tag. 575 * 576 * @param langTag The language tag of the entry, {@code null} to get 577 * the non-tagged entry. 578 * 579 * @return The middle name, {@code null} if not specified. 580 */ 581 public String getMiddleName(final LangTag langTag) { 582 583 return getStringClaim(MIDDLE_NAME_CLAIM_NAME, langTag); 584 } 585 586 587 /** 588 * Gets the middle name entries. Correspond to the {@code middle_name} 589 * claim. 590 * 591 * @return The middle name entries, empty map if none. 592 */ 593 public Map<LangTag,String> getMiddleNameEntries() { 594 595 return getLangTaggedClaim(MIDDLE_NAME_CLAIM_NAME, String.class); 596 } 597 598 599 /** 600 * Sets the middle name. Corresponds to the {@code middle_name} claim, 601 * with no language tag. 602 * 603 * @param middleName The middle name. If {@code null} the claim will be 604 * removed. 605 */ 606 public void setMiddleName(final String middleName) { 607 608 setClaim(MIDDLE_NAME_CLAIM_NAME, middleName); 609 } 610 611 612 /** 613 * Sets the middle name. Corresponds to the {@code middle_name} claim, 614 * with an optional language tag. 615 * 616 * @param middleName The middle name. If {@code null} the claim will be 617 * removed. 618 * @param langTag The language tag, {@code null} if not specified. 619 */ 620 public void setMiddleName(final String middleName, final LangTag langTag) { 621 622 setClaim(MIDDLE_NAME_CLAIM_NAME, middleName, langTag); 623 } 624 625 626 /** 627 * Gets the casual name. Corresponds to the {@code nickname} claim, 628 * with no language tag. 629 * 630 * @return The casual name, {@code null} if not specified. 631 */ 632 public String getNickname() { 633 634 return getStringClaim(NICKNAME_CLAIM_NAME); 635 } 636 637 638 /** 639 * Gets the casual name. Corresponds to the {@code nickname} claim, 640 * with an optional language tag. 641 * 642 * @param langTag The language tag of the entry, {@code null} to get 643 * the non-tagged entry. 644 * 645 * @return The casual name, {@code null} if not specified. 646 */ 647 public String getNickname(final LangTag langTag) { 648 649 return getStringClaim(NICKNAME_CLAIM_NAME, langTag); 650 } 651 652 653 /** 654 * Gets the casual name entries. Correspond to the {@code nickname} 655 * claim. 656 * 657 * @return The casual name entries, empty map if none. 658 */ 659 public Map<LangTag,String> getNicknameEntries() { 660 661 return getLangTaggedClaim(NICKNAME_CLAIM_NAME, String.class); 662 } 663 664 665 /** 666 * Sets the casual name. Corresponds to the {@code nickname} claim, 667 * with no language tag. 668 * 669 * @param nickname The casual name. If {@code null} the claim will be 670 * removed. 671 */ 672 public void setNickname(final String nickname) { 673 674 setClaim(NICKNAME_CLAIM_NAME, nickname); 675 } 676 677 678 /** 679 * Sets the casual name. Corresponds to the {@code nickname} claim, 680 * with an optional language tag. 681 * 682 * @param nickname The casual name. If {@code null} the claim will be 683 * removed. 684 * @param langTag The language tag, {@code null} if not specified. 685 */ 686 public void setNickname(final String nickname, final LangTag langTag) { 687 688 setClaim(NICKNAME_CLAIM_NAME, nickname, langTag); 689 } 690 691 692 /** 693 * Gets the preferred username. Corresponds to the 694 * {@code preferred_username} claim. 695 * 696 * @return The preferred username, {@code null} if not specified. 697 */ 698 public String getPreferredUsername() { 699 700 return getStringClaim(PREFERRED_USERNAME_CLAIM_NAME); 701 } 702 703 704 /** 705 * Sets the preferred username. Corresponds to the 706 * {@code preferred_username} claim. 707 * 708 * @param preferredUsername The preferred username. If {@code null} the 709 * claim will be removed. 710 */ 711 public void setPreferredUsername(final String preferredUsername) { 712 713 setClaim(PREFERRED_USERNAME_CLAIM_NAME, preferredUsername); 714 } 715 716 717 /** 718 * Gets the profile page. Corresponds to the {@code profile} claim. 719 * 720 * @return The profile page URI, {@code null} if not specified. 721 */ 722 public URI getProfile() { 723 724 return getURIClaim(PROFILE_CLAIM_NAME); 725 } 726 727 728 /** 729 * Sets the profile page. Corresponds to the {@code profile} claim. 730 * 731 * @param profile The profile page URI. If {@code null} the claim will 732 * be removed. 733 */ 734 public void setProfile(final URI profile) { 735 736 setURIClaim(PROFILE_CLAIM_NAME, profile); 737 } 738 739 740 /** 741 * Gets the picture. Corresponds to the {@code picture} claim. 742 * 743 * @return The picture URI, {@code null} if not specified. 744 */ 745 public URI getPicture() { 746 747 return getURIClaim(PICTURE_CLAIM_NAME); 748 } 749 750 751 /** 752 * Sets the picture. Corresponds to the {@code picture} claim. 753 * 754 * @param picture The picture URI. If {@code null} the claim will be 755 * removed. 756 */ 757 public void setPicture(final URI picture) { 758 759 setURIClaim(PICTURE_CLAIM_NAME, picture); 760 } 761 762 763 /** 764 * Gets the web page or blog. Corresponds to the {@code website} claim. 765 * 766 * @return The web page or blog URI, {@code null} if not specified. 767 */ 768 public URI getWebsite() { 769 770 return getURIClaim(WEBSITE_CLAIM_NAME); 771 } 772 773 774 /** 775 * Sets the web page or blog. Corresponds to the {@code website} claim. 776 * 777 * @param website The web page or blog URI. If {@code null} the claim 778 * will be removed. 779 */ 780 public void setWebsite(final URI website) { 781 782 setURIClaim(WEBSITE_CLAIM_NAME, website); 783 } 784 785 786 /** 787 * Gets the preferred email address. Corresponds to the {@code email} 788 * claim. 789 * 790 * <p>Use {@link #getEmailAddress()} instead. 791 * 792 * @return The preferred email address, {@code null} if not specified. 793 */ 794 @Deprecated 795 public InternetAddress getEmail() { 796 797 return getEmailClaim(EMAIL_CLAIM_NAME); 798 } 799 800 801 /** 802 * Sets the preferred email address. Corresponds to the {@code email} 803 * claim. 804 * 805 * <p>Use {@link #setEmailAddress(String)} instead. 806 * 807 * @param email The preferred email address. If {@code null} the claim 808 * will be removed. 809 */ 810 @Deprecated 811 public void setEmail(final InternetAddress email) { 812 813 setEmailClaim(EMAIL_CLAIM_NAME, email); 814 } 815 816 817 /** 818 * Gets the preferred email address. Corresponds to the {@code email} 819 * claim. 820 * 821 * @return The preferred email address, {@code null} if not specified. 822 */ 823 public String getEmailAddress() { 824 825 return getStringClaim(EMAIL_CLAIM_NAME); 826 } 827 828 829 /** 830 * Sets the preferred email address. Corresponds to the {@code email} 831 * claim. 832 * 833 * @param email The preferred email address. If {@code null} the claim 834 * will be removed. 835 */ 836 public void setEmailAddress(final String email) { 837 838 setClaim(EMAIL_CLAIM_NAME, email); 839 } 840 841 842 /** 843 * Gets the email verification status. Corresponds to the 844 * {@code email_verified} claim. 845 * 846 * @return The email verification status, {@code null} if not 847 * specified. 848 */ 849 public Boolean getEmailVerified() { 850 851 return getBooleanClaim(EMAIL_VERIFIED_CLAIM_NAME); 852 } 853 854 855 /** 856 * Sets the email verification status. Corresponds to the 857 * {@code email_verified} claim. 858 * 859 * @param emailVerified The email verification status. If {@code null} 860 * the claim will be removed. 861 */ 862 public void setEmailVerified(final Boolean emailVerified) { 863 864 setClaim(EMAIL_VERIFIED_CLAIM_NAME, emailVerified); 865 } 866 867 868 /** 869 * Gets the gender. Corresponds to the {@code gender} claim. 870 * 871 * @return The gender, {@code null} if not specified. 872 */ 873 public Gender getGender() { 874 875 String value = getStringClaim(GENDER_CLAIM_NAME); 876 877 if (value == null) 878 return null; 879 880 return new Gender(value); 881 } 882 883 884 /** 885 * Sets the gender. Corresponds to the {@code gender} claim. 886 * 887 * @param gender The gender. If {@code null} the claim will be removed. 888 */ 889 public void setGender(final Gender gender) { 890 891 if (gender != null) 892 setClaim(GENDER_CLAIM_NAME, gender.getValue()); 893 else 894 setClaim(GENDER_CLAIM_NAME, null); 895 } 896 897 898 /** 899 * Gets the date of birth. Corresponds to the {@code birthdate} claim. 900 * 901 * @return The date of birth, {@code null} if not specified. 902 */ 903 public String getBirthdate() { 904 905 return getStringClaim(BIRTHDATE_CLAIM_NAME); 906 } 907 908 909 /** 910 * Sets the date of birth. Corresponds to the {@code birthdate} claim. 911 * 912 * @param birthdate The date of birth. If {@code null} the claim will 913 * be removed. 914 */ 915 public void setBirthdate(final String birthdate) { 916 917 setClaim(BIRTHDATE_CLAIM_NAME, birthdate); 918 } 919 920 921 /** 922 * Gets the zoneinfo. Corresponds to the {@code zoneinfo} claim. 923 * 924 * @return The zoneinfo, {@code null} if not specified. 925 */ 926 public String getZoneinfo() { 927 928 return getStringClaim(ZONEINFO_CLAIM_NAME); 929 } 930 931 932 /** 933 * Sets the zoneinfo. Corresponds to the {@code zoneinfo} claim. 934 * 935 * @param zoneinfo The zoneinfo. If {@code null} the claim will be 936 * removed. 937 */ 938 public void setZoneinfo(final String zoneinfo) { 939 940 setClaim(ZONEINFO_CLAIM_NAME, zoneinfo); 941 } 942 943 944 /** 945 * Gets the locale. Corresponds to the {@code locale} claim. 946 * 947 * @return The locale, {@code null} if not specified. 948 */ 949 public String getLocale() { 950 951 return getStringClaim(LOCALE_CLAIM_NAME); 952 } 953 954 955 /** 956 * Sets the locale. Corresponds to the {@code locale} claim. 957 * 958 * @param locale The locale. If {@code null} the claim will be 959 * removed. 960 */ 961 public void setLocale(final String locale) { 962 963 setClaim(LOCALE_CLAIM_NAME, locale); 964 } 965 966 967 /** 968 * Gets the preferred telephone number. Corresponds to the 969 * {@code phone_number} claim. 970 * 971 * @return The preferred telephone number, {@code null} if not 972 * specified. 973 */ 974 public String getPhoneNumber() { 975 976 return getStringClaim(PHONE_NUMBER_CLAIM_NAME); 977 } 978 979 980 /** 981 * Sets the preferred telephone number. Corresponds to the 982 * {@code phone_number} claim. 983 * 984 * @param phoneNumber The preferred telephone number. If {@code null} 985 * the claim will be removed. 986 */ 987 public void setPhoneNumber(final String phoneNumber) { 988 989 setClaim(PHONE_NUMBER_CLAIM_NAME, phoneNumber); 990 } 991 992 993 /** 994 * Gets the phone number verification status. Corresponds to the 995 * {@code phone_number_verified} claim. 996 * 997 * @return The phone number verification status, {@code null} if not 998 * specified. 999 */ 1000 public Boolean getPhoneNumberVerified() { 1001 1002 return getBooleanClaim(PHONE_NUMBER_VERIFIED_CLAIM_NAME); 1003 } 1004 1005 1006 /** 1007 * Sets the email verification status. Corresponds to the 1008 * {@code phone_number_verified} claim. 1009 * 1010 * @param phoneNumberVerified The phone number verification status. If 1011 * {@code null} the claim will be removed. 1012 */ 1013 public void setPhoneNumberVerified(final Boolean phoneNumberVerified) { 1014 1015 setClaim(PHONE_NUMBER_VERIFIED_CLAIM_NAME, phoneNumberVerified); 1016 } 1017 1018 1019 /** 1020 * Gets the preferred address. Corresponds to the {@code address} 1021 * claim, with no language tag. 1022 * 1023 * @return The preferred address, {@code null} if not specified. 1024 */ 1025 public Address getAddress() { 1026 1027 return getAddress(null); 1028 } 1029 1030 1031 /** 1032 * Gets the preferred address. Corresponds to the {@code address} 1033 * claim, with an optional language tag. 1034 * 1035 * @param langTag The language tag of the entry, {@code null} to get 1036 * the non-tagged entry. 1037 * 1038 * @return The preferred address, {@code null} if not specified. 1039 */ 1040 public Address getAddress(final LangTag langTag) { 1041 1042 String name; 1043 1044 if (langTag!= null) 1045 name = ADDRESS_CLAIM_NAME + "#" + langTag; 1046 else 1047 name = ADDRESS_CLAIM_NAME; 1048 1049 JSONObject jsonObject = getClaim(name, JSONObject.class); 1050 1051 if (jsonObject == null) 1052 return null; 1053 1054 return new Address(jsonObject); 1055 } 1056 1057 1058 /** 1059 * Gets the preferred address entries. Correspond to the 1060 * {@code address} claim. 1061 * 1062 * @return The preferred address entries, empty map if none. 1063 */ 1064 public Map<LangTag,Address> getAddressEntries() { 1065 1066 Map<LangTag,JSONObject> entriesIn = getLangTaggedClaim(ADDRESS_CLAIM_NAME, JSONObject.class); 1067 1068 Map<LangTag,Address> entriesOut = new HashMap<>(); 1069 1070 for (Map.Entry<LangTag,JSONObject> en: entriesIn.entrySet()) 1071 entriesOut.put(en.getKey(), new Address(en.getValue())); 1072 1073 return entriesOut; 1074 } 1075 1076 1077 /** 1078 * Sets the preferred address. Corresponds to the {@code address} 1079 * claim, with no language tag. 1080 * 1081 * @param address The preferred address. If {@code null} the claim will 1082 * be removed. 1083 */ 1084 public void setAddress(final Address address) { 1085 1086 if (address != null) 1087 setClaim(ADDRESS_CLAIM_NAME, address.toJSONObject()); 1088 else 1089 setClaim(ADDRESS_CLAIM_NAME, null); 1090 } 1091 1092 1093 /** 1094 * Sets the preferred address. Corresponds to the {@code address} 1095 * claim, with an optional language tag. 1096 * 1097 * @param address The preferred address. If {@code null} the claim 1098 * will be removed. 1099 * @param langTag The language tag, {@code null} if not specified. 1100 */ 1101 public void setAddress(final Address address, final LangTag langTag) { 1102 1103 String key = langTag == null ? ADDRESS_CLAIM_NAME : ADDRESS_CLAIM_NAME + "#" + langTag; 1104 1105 if (address != null) 1106 setClaim(key, address.toJSONObject()); 1107 else 1108 setClaim(key, null); 1109 } 1110 1111 1112 /** 1113 * Gets the time the end-user information was last updated. Corresponds 1114 * to the {@code updated_at} claim. 1115 * 1116 * @return The time the end-user information was last updated, 1117 * {@code null} if not specified. 1118 */ 1119 public Date getUpdatedTime() { 1120 1121 return getDateClaim(UPDATED_AT_CLAIM_NAME); 1122 } 1123 1124 1125 /** 1126 * Sets the time the end-user information was last updated. Corresponds 1127 * to the {@code updated_at} claim. 1128 * 1129 * @param updatedTime The time the end-user information was last 1130 * updated. If {@code null} the claim will be 1131 * removed. 1132 */ 1133 public void setUpdatedTime(final Date updatedTime) { 1134 1135 setDateClaim(UPDATED_AT_CLAIM_NAME, updatedTime); 1136 } 1137 1138 1139 /** 1140 * Adds the specified aggregated claims provided by an external claims 1141 * source. 1142 * 1143 * @param aggregatedClaims The aggregated claims instance, if 1144 * {@code null} nothing will be added. 1145 */ 1146 public void addAggregatedClaims(final AggregatedClaims aggregatedClaims) { 1147 1148 if (aggregatedClaims == null) { 1149 return; 1150 } 1151 1152 aggregatedClaims.mergeInto(claims); 1153 } 1154 1155 1156 /** 1157 * Gets the included aggregated claims provided by each external claims 1158 * source. 1159 * 1160 * @return The aggregated claims, {@code null} if none are found. 1161 */ 1162 public Set<AggregatedClaims> getAggregatedClaims() { 1163 1164 Map<String,JSONObject> claimSources = ExternalClaimsUtils.getExternalClaimSources(claims); 1165 1166 if (claimSources == null) { 1167 return null; // No external _claims_sources 1168 } 1169 1170 Set<AggregatedClaims> aggregatedClaimsSet = new HashSet<>(); 1171 1172 for (Map.Entry<String,JSONObject> en: claimSources.entrySet()) { 1173 1174 String sourceID = en.getKey(); 1175 JSONObject sourceSpec = en.getValue(); 1176 1177 Object jwtValue = sourceSpec.get("JWT"); 1178 if (! (jwtValue instanceof String)) { 1179 continue; // skip 1180 } 1181 1182 JWT claimsJWT; 1183 try { 1184 claimsJWT = JWTParser.parse((String)jwtValue); 1185 } catch (java.text.ParseException e) { 1186 continue; // invalid JWT, skip 1187 } 1188 1189 Set<String> claimNames = ExternalClaimsUtils.getExternalClaimNamesForSource(claims, sourceID); 1190 1191 if (claimNames.isEmpty()) { 1192 continue; // skip 1193 } 1194 1195 aggregatedClaimsSet.add(new AggregatedClaims(sourceID, claimNames, claimsJWT)); 1196 } 1197 1198 if (aggregatedClaimsSet.isEmpty()) { 1199 return null; 1200 } 1201 1202 return aggregatedClaimsSet; 1203 } 1204 1205 1206 /** 1207 * Adds the specified distributed claims from an external claims source. 1208 * 1209 * @param distributedClaims The distributed claims instance, if 1210 * {@code null} nothing will be added. 1211 */ 1212 public void addDistributedClaims(final DistributedClaims distributedClaims) { 1213 1214 if (distributedClaims == null) { 1215 return; 1216 } 1217 1218 distributedClaims.mergeInto(claims); 1219 } 1220 1221 1222 /** 1223 * Gets the included distributed claims provided by each external 1224 * claims source. 1225 * 1226 * @return The distributed claims, {@code null} if none are found. 1227 */ 1228 public Set<DistributedClaims> getDistributedClaims() { 1229 1230 Map<String,JSONObject> claimSources = ExternalClaimsUtils.getExternalClaimSources(claims); 1231 1232 if (claimSources == null) { 1233 return null; // No external _claims_sources 1234 } 1235 1236 Set<DistributedClaims> distributedClaimsSet = new HashSet<>(); 1237 1238 for (Map.Entry<String,JSONObject> en: claimSources.entrySet()) { 1239 1240 String sourceID = en.getKey(); 1241 JSONObject sourceSpec = en.getValue(); 1242 1243 Object endpointValue = sourceSpec.get("endpoint"); 1244 if (! (endpointValue instanceof String)) { 1245 continue; // skip 1246 } 1247 1248 URI endpoint; 1249 try { 1250 endpoint = new URI((String)endpointValue); 1251 } catch (URISyntaxException e) { 1252 continue; // invalid URI, skip 1253 } 1254 1255 AccessToken accessToken = null; 1256 Object accessTokenValue = sourceSpec.get("access_token"); 1257 if (accessTokenValue instanceof String) { 1258 accessToken = new TypelessAccessToken((String)accessTokenValue); 1259 } 1260 1261 Set<String> claimNames = ExternalClaimsUtils.getExternalClaimNamesForSource(claims, sourceID); 1262 1263 if (claimNames.isEmpty()) { 1264 continue; // skip 1265 } 1266 1267 distributedClaimsSet.add(new DistributedClaims(sourceID, claimNames, endpoint, accessToken)); 1268 } 1269 1270 if (distributedClaimsSet.isEmpty()) { 1271 return null; 1272 } 1273 1274 return distributedClaimsSet; 1275 } 1276 1277 1278 /** 1279 * Parses a UserInfo claims set from the specified JSON object string. 1280 * 1281 * @param json The JSON object string to parse. Must not be 1282 * {@code null}. 1283 * 1284 * @return The UserInfo claims set. 1285 * 1286 * @throws ParseException If parsing failed. 1287 */ 1288 public static UserInfo parse(final String json) 1289 throws ParseException { 1290 1291 JSONObject jsonObject = JSONObjectUtils.parse(json); 1292 1293 try { 1294 return new UserInfo(jsonObject); 1295 1296 } catch (IllegalArgumentException e) { 1297 1298 throw new ParseException(e.getMessage(), e); 1299 } 1300 } 1301}