001package com.nimbusds.openid.connect.sdk; 002 003 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Iterator; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.LinkedList; 010import java.util.List; 011import java.util.Map; 012import java.util.Set; 013 014import net.jcip.annotations.Immutable; 015 016import org.apache.commons.lang3.tuple.ImmutablePair; 017 018import net.minidev.json.JSONObject; 019 020import com.nimbusds.langtag.LangTag; 021import com.nimbusds.langtag.LangTagException; 022 023import com.nimbusds.oauth2.sdk.Scope; 024 025import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement; 026 027 028/** 029 * Specifies the individual claims to return from the UserInfo endpoint and / 030 * or in the ID Token. 031 * 032 * <p>Related specifications: 033 * 034 * <ul> 035 * <li>OpenID Connect Messages, section 2.6. 036 * </ul> 037 * 038 * @author Vladimir Dzhuvinov 039 */ 040public class ClaimsRequest { 041 042 043 /** 044 * Individual claim request. This class is immutable. 045 * 046 * <p>Related specifications: 047 * 048 * <ul> 049 * <li>OpenID Connect Messages, section 2.6.1. 050 * </ul> 051 */ 052 @Immutable 053 public static class Entry { 054 055 056 /** 057 * The claim name. 058 */ 059 private final String claimName; 060 061 062 /** 063 * The claim requirement. 064 */ 065 private final ClaimRequirement requirement; 066 067 068 /** 069 * Optional language tag. 070 */ 071 private final LangTag langTag; 072 073 074 /** 075 * Optional claim value. 076 */ 077 private final String value; 078 079 080 /** 081 * Optional claim values. 082 */ 083 private final List<String> values; 084 085 086 /** 087 * Creates a new individual claim request. The claim 088 * requirement is set to voluntary (the default) and no 089 * expected value(s) are specified. 090 * 091 * @param claimName The claim name. Must not be {@code null}. 092 * @param langTag Optional language tag for the claim. 093 */ 094 public Entry(final String claimName, final LangTag langTag) { 095 096 this(claimName, ClaimRequirement.VOLUNTARY, langTag, (String)null, null); 097 } 098 099 100 /** 101 * Creates a new individual claim request. 102 * 103 * @param claimName The claim name. Must not be {@code null}. 104 * @param requirement The claim requirement. Must not be 105 * {@code null}. 106 */ 107 public Entry(final String claimName, final ClaimRequirement requirement) { 108 109 this(claimName, requirement, null, null, null); 110 } 111 112 113 /** 114 * Creates a new individual claim request. 115 * 116 * @param claimName The claim name. Must not be {@code null}. 117 * @param requirement The claim requirement. Must not be 118 * {@code null}. 119 * @param langTag Optional language tag for the claim. 120 * @param value Optional expected value for the claim. 121 */ 122 public Entry(final String claimName, final ClaimRequirement requirement, 123 final LangTag langTag, final String value) { 124 125 this(claimName, requirement, langTag, value, null); 126 } 127 128 129 /** 130 * Creates a new individual claim request. 131 * 132 * @param claimName The claim name. Must not be {@code null}. 133 * @param requirement The claim requirement. Must not be 134 * {@code null}. 135 * @param langTag Optional language tag for the claim. 136 * @param values Optional expected values for the claim. 137 */ 138 public Entry(final String claimName, final ClaimRequirement requirement, 139 final LangTag langTag, final List<String> values) { 140 141 this(claimName, requirement, langTag, null, values); 142 } 143 144 145 /** 146 * Creates a new individual claim request. This constructor is 147 * to be used privately. Ensures that {@code value} and 148 * {@code values} are not simultaneously specified. 149 * 150 * @param claimName The claim name. Must not be {@code null}. 151 * @param requirement The claim requirement. Must not be 152 * {@code null}. 153 * @param langTag Optional language tag for the claim. 154 * @param value Optional expected value for the claim. If 155 * set, then the {@code values} parameter 156 * must not be set. 157 * @param values Optional expected values for the claim. 158 * If set, then the {@code value} parameter 159 * must not be set. 160 */ 161 private Entry(final String claimName, final ClaimRequirement requirement, final LangTag langTag, 162 final String value, final List<String> values) { 163 164 if (claimName == null) 165 throw new IllegalArgumentException("The claim name must not be null"); 166 167 this.claimName = claimName; 168 169 170 if (requirement == null) 171 throw new IllegalArgumentException("The claim requirement must not be null"); 172 173 this.requirement = requirement; 174 175 176 this.langTag = langTag; 177 178 179 if (value != null && values == null) { 180 181 this.value = value; 182 this.values = null; 183 184 } else if (value == null && values != null) { 185 186 this.value = null; 187 this.values = values; 188 189 } else if (value == null && values == null) { 190 191 this.value = null; 192 this.values = null; 193 194 } else { 195 196 throw new IllegalArgumentException("Either value or values must be specified, but not both"); 197 } 198 } 199 200 201 /** 202 * Gets the claim name. 203 * 204 * @return The claim name. 205 */ 206 public String getClaimName() { 207 208 return claimName; 209 } 210 211 212 /** 213 * Gets the claim name, optionally with the language tag 214 * appended. 215 * 216 * <p>Example with language tag: 217 * 218 * <pre> 219 * name#de-DE 220 * </pre> 221 * 222 * @param withLangTag If {@code true} the language tag will be 223 * appended to the name (if any), else not. 224 * 225 * @return The claim name, with optionally appended language 226 * tag. 227 */ 228 public String getClaimName(final boolean withLangTag) { 229 230 if (withLangTag && langTag != null) 231 return claimName + "#" + langTag.toString(); 232 else 233 return claimName; 234 } 235 236 237 /** 238 * Gets the claim requirement. 239 * 240 * @return The claim requirement. 241 */ 242 public ClaimRequirement getClaimRequirement() { 243 244 return requirement; 245 } 246 247 248 /** 249 * Gets the optional language tag for the claim. 250 * 251 * @return The language tag, {@code null} if not specified. 252 */ 253 public LangTag getLangTag() { 254 255 return langTag; 256 } 257 258 259 /** 260 * Gets the optional value for the claim. 261 * 262 * @return The value, {@code null} if not specified. 263 */ 264 public String getValue() { 265 266 return value; 267 } 268 269 270 /** 271 * Gets the optional values for the claim. 272 * 273 * @return The values, {@code null} if not specified. 274 */ 275 public List<String> getValues() { 276 277 return values; 278 } 279 280 281 /** 282 * Returns the JSON object representation of the specified 283 * collection of individual claim requests. 284 * 285 * <p>Example: 286 * 287 * <pre> 288 * { 289 * "given_name": {"essential": true}, 290 * "nickname": null, 291 * "email": {"essential": true}, 292 * "email_verified": {"essential": true}, 293 * "picture": null, 294 * "http://example.info/claims/groups": null 295 * } 296 * </pre> 297 * 298 * @param entries The entries to serialise. Must not be 299 * {@code null}. 300 * 301 * @return The corresponding JSON object, empty if no claims 302 * were found. 303 */ 304 public static JSONObject toJSONObject(final Collection<Entry> entries) { 305 306 JSONObject o = new JSONObject(); 307 308 for (Entry entry: entries) { 309 310 // Compose the optional value 311 JSONObject entrySpec = null; 312 313 if (entry.getValue() != null) { 314 315 entrySpec = new JSONObject(); 316 entrySpec.put("value", entry.getValue()); 317 } 318 319 if (entry.getValues() != null) { 320 321 // Either "value" or "values", or none 322 // may be defined 323 entrySpec = new JSONObject(); 324 entrySpec.put("values", entry.getValues()); 325 } 326 327 if (entry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) { 328 329 if (entrySpec == null) 330 entrySpec = new JSONObject(); 331 332 entrySpec.put("essential", true); 333 } 334 335 o.put(entry.getClaimName(true), entrySpec); 336 } 337 338 return o; 339 } 340 341 342 /** 343 * Parses a collection of individual claim requests from the 344 * specified JSON object. Request entries that are not 345 * understood are silently ignored. 346 */ 347 public static Collection<Entry> parseEntries(final JSONObject jsonObject) { 348 349 Collection<Entry> entries = new LinkedList<Entry>(); 350 351 if (jsonObject.isEmpty()) 352 return entries; 353 354 for (Map.Entry<String,Object> member: jsonObject.entrySet()) { 355 356 // Process the key 357 String claimNameWithOptLangTag = member.getKey(); 358 359 String claimName; 360 LangTag langTag = null; 361 362 if (claimNameWithOptLangTag.contains("#")) { 363 364 String[] parts = claimNameWithOptLangTag.split("#", 2); 365 366 claimName = parts[0]; 367 368 try { 369 langTag = LangTag.parse(parts[1]); 370 371 } catch (LangTagException e) { 372 373 // Ignore and continue 374 continue; 375 } 376 377 } else { 378 claimName = claimNameWithOptLangTag; 379 } 380 381 // Parse the optional value 382 if (member.getValue() == null) { 383 384 // Voluntary claim with no value(s) 385 entries.add(new Entry(claimName, langTag)); 386 continue; 387 } 388 389 try { 390 JSONObject entrySpec = (JSONObject)member.getValue(); 391 392 ClaimRequirement requirement = ClaimRequirement.VOLUNTARY; 393 394 if (entrySpec.containsKey("essential")) { 395 396 boolean isEssential = (Boolean)entrySpec.get("essential"); 397 398 if (isEssential) 399 requirement = ClaimRequirement.ESSENTIAL; 400 } 401 402 403 if (entrySpec.containsKey("value")) { 404 405 String expectedValue = (String)entrySpec.get("value"); 406 407 entries.add(new Entry(claimName, requirement, langTag, expectedValue)); 408 409 } else if (entrySpec.containsKey("values")) { 410 411 412 List<String> expectedValues = new LinkedList<String>(); 413 414 for (Object v: (List)entrySpec.get("values")) { 415 416 expectedValues.add((String)v); 417 } 418 419 entries.add(new Entry(claimName, requirement, langTag, expectedValues)); 420 } 421 422 } catch (Exception e) { 423 424 // Ignore and continue 425 continue; 426 } 427 } 428 429 return entries; 430 } 431 } 432 433 434 /** 435 * The requested ID token claims, keyed by claim name and language tag. 436 */ 437 private final Map<ImmutablePair<String,LangTag>,Entry> idTokenClaims = 438 new HashMap<ImmutablePair<String,LangTag>,Entry>(); 439 440 441 /** 442 * The requested UserInfo claims, keyed by claim name and language tag. 443 */ 444 private final Map<ImmutablePair<String,LangTag>,Entry> userInfoClaims = 445 new HashMap<ImmutablePair<String,LangTag>,Entry>(); 446 447 448 /** 449 * Creates a new empty claims request. 450 */ 451 public ClaimsRequest() { 452 453 // Nothing to initialise 454 } 455 456 457 /** 458 * Adds the entries from the specified other claims request. 459 * 460 * @param other The other claims request. If {@code null} no claims 461 * request entries will be added to this claims request. 462 */ 463 public void add(final ClaimsRequest other) { 464 465 if (other == null) 466 return; 467 468 idTokenClaims.putAll(other.idTokenClaims); 469 userInfoClaims.putAll(other.userInfoClaims); 470 } 471 472 473 /** 474 * Adds the specified ID token claim to the request. It is marked as 475 * voluntary and no language tag and value(s) are associated with it. 476 * 477 * @param claimName The claim name. Must not be {@code null}. 478 */ 479 public void addIDTokenClaim(final String claimName) { 480 481 addIDTokenClaim(claimName, ClaimRequirement.VOLUNTARY); 482 } 483 484 485 /** 486 * Adds the specified ID token claim to the request. No language tag 487 * and value(s) are associated with it. 488 * 489 * @param claimName The claim name. Must not be {@code null}. 490 * @param requirement The claim requirement. Must not be {@code null}. 491 */ 492 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement) { 493 494 addIDTokenClaim(claimName, requirement, null); 495 } 496 497 498 /** 499 * Adds the specified ID token claim to the request. No value(s) are 500 * associated with it. 501 * 502 * @param claimName The claim name. Must not be {@code null}. 503 * @param requirement The claim requirement. Must not be {@code null}. 504 * @param langTag The associated language tag, {@code null} if not 505 * specified. 506 */ 507 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 508 final LangTag langTag) { 509 510 511 addIDTokenClaim(claimName, requirement, langTag, (String)null); 512 } 513 514 515 /** 516 * Adds the specified ID token claim to the request. 517 * 518 * @param claimName The claim name. Must not be {@code null}. 519 * @param requirement The claim requirement. Must not be {@code null}. 520 * @param langTag The associated language tag, {@code null} if not 521 * specified. 522 * @param value The expected claim value, {@code null} if not 523 * specified. 524 */ 525 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 526 final LangTag langTag, final String value) { 527 528 addIDTokenClaim(new Entry(claimName, requirement, langTag, value)); 529 } 530 531 532 /** 533 * Adds the specified ID token claim to the request. 534 * 535 * @param claimName The claim name. Must not be {@code null}. 536 * @param requirement The claim requirement. Must not be {@code null}. 537 * @param langTag The associated language tag, {@code null} if not 538 * specified. 539 * @param values The expected claim values, {@code null} if not 540 * specified. 541 */ 542 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 543 final LangTag langTag, final List<String> values) { 544 545 addIDTokenClaim(new Entry(claimName, requirement, langTag, values)); 546 } 547 548 549 /** 550 * Adds the specified ID token claim to the request. 551 * 552 * @param entry The individual ID token claim request. Must not be 553 * {@code null}. 554 */ 555 public void addIDTokenClaim(final Entry entry) { 556 557 ImmutablePair<String,LangTag> key = 558 new ImmutablePair<String,LangTag>(entry.getClaimName(), entry.getLangTag()); 559 560 idTokenClaims.put(key, entry); 561 } 562 563 564 /** 565 * Gets the requested ID token claims. 566 * 567 * @return The ID token claims, as an unmodifiable collection, empty 568 * set if none. 569 */ 570 public Collection<Entry> getIDTokenClaims() { 571 572 return Collections.unmodifiableCollection(idTokenClaims.values()); 573 } 574 575 576 /** 577 * Gets the names of the requested ID token claim names. 578 * 579 * @param withLangTag If {@code true} the language tags, if any, will 580 * be appended to the names, else not. 581 * 582 * 583 * @return The ID token claim names, as an unmodifiable set, empty set 584 * if none. 585 */ 586 public Set<String> getIDTokenClaimNames(final boolean withLangTag) { 587 588 Set<String> names = new HashSet<String>(); 589 590 for (Entry en: idTokenClaims.values()) 591 names.add(en.getClaimName(withLangTag)); 592 593 return Collections.unmodifiableSet(names); 594 } 595 596 597 /** 598 * Removes the specified ID token claim from the request. 599 * 600 * @param claimName The claim name. Must not be {@code null}. 601 * @param langTag The associated language tag, {@code null} if none. 602 * 603 * @return The removed ID token claim, {@code null} if not found. 604 */ 605 public Entry removeIDTokenClaim(final String claimName, final LangTag langTag) { 606 607 ImmutablePair<String,LangTag> key = 608 new ImmutablePair<String,LangTag>(claimName, langTag); 609 610 return idTokenClaims.remove(key); 611 } 612 613 614 /** 615 * Removes the specified ID token claims from the request, in all 616 * existing language tag variations. 617 * 618 * @param claimName The claim name. Must not be {@code null}. 619 * 620 * @return The removed ID token claims, as an unmodifiable collection, 621 * empty set if none were found. 622 */ 623 public Collection<Entry> removeIDTokenClaims(final String claimName) { 624 625 Collection<Entry> removedClaims = new LinkedList<Entry>(); 626 627 Iterator<Map.Entry<ImmutablePair<String,LangTag>,Entry>> it = idTokenClaims.entrySet().iterator(); 628 629 while (it.hasNext()) { 630 631 Map.Entry<ImmutablePair<String,LangTag>,Entry> reqEntry = it.next(); 632 633 if (reqEntry.getKey().getLeft().equals(claimName)) { 634 635 removedClaims.add(reqEntry.getValue()); 636 637 it.remove(); 638 } 639 } 640 641 return Collections.unmodifiableCollection(removedClaims); 642 } 643 644 645 /** 646 * Adds the specified UserInfo claim to the request. It is marked as 647 * voluntary and no language tag and value(s) are associated with it. 648 * 649 * @param claimName The claim name. Must not be {@code null}. 650 */ 651 public void addUserInfoClaim(final String claimName) { 652 653 addUserInfoClaim(claimName, ClaimRequirement.VOLUNTARY); 654 } 655 656 657 /** 658 * Adds the specified UserInfo claim to the request. No language tag 659 * and value(s) are associated with it. 660 * 661 * @param claimName The claim name. Must not be {@code null}. 662 * @param requirement The claim requirement. Must not be {@code null}. 663 */ 664 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement) { 665 666 addUserInfoClaim(claimName, requirement, null); 667 } 668 669 670 /** 671 * Adds the specified UserInfo claim to the request. No value(s) are 672 * associated with it. 673 * 674 * @param claimName The claim name. Must not be {@code null}. 675 * @param requirement The claim requirement. Must not be {@code null}. 676 * @param langTag The associated language tag, {@code null} if not 677 * specified. 678 */ 679 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 680 final LangTag langTag) { 681 682 683 addUserInfoClaim(claimName, requirement, langTag, (String)null); 684 } 685 686 687 /** 688 * Adds the specified UserInfo claim to the request. 689 * 690 * @param claimName The claim name. Must not be {@code null}. 691 * @param requirement The claim requirement. Must not be {@code null}. 692 * @param langTag The associated language tag, {@code null} if not 693 * specified. 694 * @param value The expected claim value, {@code null} if not 695 * specified. 696 */ 697 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 698 final LangTag langTag, final String value) { 699 700 addUserInfoClaim(new Entry(claimName, requirement, langTag, value)); 701 } 702 703 704 /** 705 * Adds the specified UserInfo claim to the request. 706 * 707 * @param claimName The claim name. Must not be {@code null}. 708 * @param requirement The claim requirement. Must not be {@code null}. 709 * @param langTag The associated language tag, {@code null} if not 710 * specified. 711 * @param values The expected claim values, {@code null} if not 712 * specified. 713 */ 714 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 715 final LangTag langTag, final List<String> values) { 716 717 addUserInfoClaim(new Entry(claimName, requirement, langTag, values)); 718 } 719 720 721 /** 722 * Adds the specified UserInfo claim to the request. 723 * 724 * @param entry The individual UserInfo claim request. Must not be 725 * {@code null}. 726 */ 727 public void addUserInfoClaim(final Entry entry) { 728 729 ImmutablePair<String,LangTag> key = 730 new ImmutablePair<String,LangTag>(entry.getClaimName(), entry.getLangTag()); 731 732 userInfoClaims.put(key, entry); 733 } 734 735 736 /** 737 * Gets the requested UserInfo claims. 738 * 739 * @return The UserInfo claims, as an unmodifiable collection, empty 740 * set if none. 741 */ 742 public Collection<Entry> getUserInfoClaims() { 743 744 return Collections.unmodifiableCollection(userInfoClaims.values()); 745 } 746 747 748 /** 749 * Gets the names of the requested UserInfo claim names. 750 * 751 * @param withLangTag If {@code true} the language tags, if any, will 752 * be appended to the names, else not. 753 * 754 * 755 * @return The UserInfo claim names, as an unmodifiable set, empty set 756 * if none. 757 */ 758 public Set<String> getUserInfoClaimNames(final boolean withLangTag) { 759 760 Set<String> names = new HashSet<String>(); 761 762 for (Entry en: userInfoClaims.values()) 763 names.add(en.getClaimName(withLangTag)); 764 765 return Collections.unmodifiableSet(names); 766 } 767 768 769 /** 770 * Removes the specified UserInfo claim from the request. 771 * 772 * @param claimName The claim name. Must not be {@code null}. 773 * @param langTag The associated language tag, {@code null} if none. 774 * 775 * @return The removed UserInfo claim, {@code null} if not found. 776 */ 777 public Entry removeUserInfoClaim(final String claimName, final LangTag langTag) { 778 779 ImmutablePair<String,LangTag> key = 780 new ImmutablePair<String,LangTag>(claimName, langTag); 781 782 return userInfoClaims.remove(key); 783 } 784 785 786 /** 787 * Removes the specified UserInfo claims from the request, in all 788 * existing language tag variations. 789 * 790 * @param claimName The claim name. Must not be {@code null}. 791 * 792 * @return The removed UserInfo claims, as an unmodifiable collection, 793 * empty set if none were found. 794 */ 795 public Collection<Entry> removeUserInfoClaims(final String claimName) { 796 797 Collection<Entry> removedClaims = new LinkedList<Entry>(); 798 799 Iterator<Map.Entry<ImmutablePair<String,LangTag>,Entry>> it = userInfoClaims.entrySet().iterator(); 800 801 while (it.hasNext()) { 802 803 Map.Entry<ImmutablePair<String,LangTag>,Entry> reqEntry = it.next(); 804 805 if (reqEntry.getKey().getLeft().equals(claimName)) { 806 807 removedClaims.add(reqEntry.getValue()); 808 809 it.remove(); 810 } 811 } 812 813 return Collections.unmodifiableCollection(removedClaims); 814 } 815 816 817 /** 818 * Returns the JSON object representation of this claims request. 819 * 820 * <p>Example: 821 * 822 * <pre> 823 * { 824 * "userinfo": 825 * { 826 * "given_name": {"essential": true}, 827 * "nickname": null, 828 * "email": {"essential": true}, 829 * "email_verified": {"essential": true}, 830 * "picture": null, 831 * "http://example.info/claims/groups": null 832 * }, 833 * "id_token": 834 * { 835 * "auth_time": {"essential": true}, 836 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 837 * } 838 * } 839 * </pre> 840 * 841 * @return The corresponding JSON object, empty if no ID token and 842 * UserInfo claims are specified. 843 */ 844 public JSONObject toJSONObject() { 845 846 JSONObject o = new JSONObject(); 847 848 Collection<Entry> idTokenEntries = getIDTokenClaims(); 849 850 if (! idTokenEntries.isEmpty()) { 851 852 o.put("id_token", Entry.toJSONObject(idTokenEntries)); 853 } 854 855 Collection<Entry> userInfoEntries = getUserInfoClaims(); 856 857 if (! userInfoEntries.isEmpty()) { 858 859 o.put("user_info", Entry.toJSONObject(userInfoEntries)); 860 } 861 862 return o; 863 } 864 865 866 @Override 867 public String toString() { 868 869 return toJSONObject().toString(); 870 } 871 872 873 /** 874 * Gets the claims request for the specified scope. The scope values 875 * that are {@link OIDCScopeValue standard OpenID Connect scope values} 876 * are resolved to their respective individual claims requests, any 877 * other scope values are ignored. 878 * 879 * @param scope The scope. Must not be {@code null}. 880 * 881 * @return The claims request. 882 */ 883 public static ClaimsRequest forScope(final Scope scope) { 884 885 ClaimsRequest claimsRequest = new ClaimsRequest(); 886 887 for (Scope.Value value: scope) { 888 889 Set<ClaimsRequest.Entry> entries; 890 891 if (value.equals(OIDCScopeValue.PROFILE)) { 892 893 entries = OIDCScopeValue.PROFILE.toClaimsRequestEntries(); 894 895 } else if (value.equals(OIDCScopeValue.EMAIL)) { 896 897 entries = OIDCScopeValue.EMAIL.toClaimsRequestEntries(); 898 899 } else if (value.equals(OIDCScopeValue.PHONE)) { 900 901 entries = OIDCScopeValue.PHONE.toClaimsRequestEntries(); 902 903 } else if (value.equals(OIDCScopeValue.ADDRESS)) { 904 905 entries = OIDCScopeValue.ADDRESS.toClaimsRequestEntries(); 906 907 } else { 908 909 continue; // skip 910 } 911 912 for (ClaimsRequest.Entry en: entries) 913 claimsRequest.addUserInfoClaim(en); 914 } 915 916 return claimsRequest; 917 } 918 919 920 /** 921 * Parses a claims request from the specified JSON object 922 * representation. Unexpected members in the JSON object are silently 923 * ignored. 924 * 925 * @param jsonObject The JSON object to parse. Must not be 926 * {@code null}. 927 * 928 * @return The claims request. 929 */ 930 public static ClaimsRequest parse(final JSONObject jsonObject) { 931 932 ClaimsRequest claimsRequest = new ClaimsRequest(); 933 934 try { 935 if (jsonObject.containsKey("id_token")) { 936 937 JSONObject idTokenObject = (JSONObject)jsonObject.get("id_token"); 938 939 Collection<Entry> idTokenClaims = Entry.parseEntries(idTokenObject); 940 941 for (Entry entry: idTokenClaims) 942 claimsRequest.addIDTokenClaim(entry); 943 } 944 945 946 if (jsonObject.containsKey("user_info")) { 947 948 JSONObject userInfoObject = (JSONObject)jsonObject.get("user_info"); 949 950 Collection<Entry> userInfoClaims = Entry.parseEntries(userInfoObject); 951 952 for (Entry entry: userInfoClaims) 953 claimsRequest.addUserInfoClaim(entry); 954 } 955 956 } catch (Exception e) { 957 958 // Ignore 959 } 960 961 return claimsRequest; 962 } 963 964}