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