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