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.ParseException; 025import com.nimbusds.oauth2.sdk.Scope; 026import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 027 028import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement; 029 030 031/** 032 * Specifies the individual claims to return from the UserInfo endpoint and / 033 * or in the ID Token. 034 * 035 * <p>Related specifications: 036 * 037 * <ul> 038 * <li>OpenID Connect Core 1.0, section 5.5. 039 * </ul> 040 */ 041public class ClaimsRequest { 042 043 044 /** 045 * Individual claim request. 046 * 047 * <p>Related specifications: 048 * 049 * <ul> 050 * <li>OpenID Connect Core 1.0, section 5.5.1. 051 * </ul> 052 */ 053 @Immutable 054 public static class Entry { 055 056 057 /** 058 * The claim name. 059 */ 060 private final String claimName; 061 062 063 /** 064 * The claim requirement. 065 */ 066 private final ClaimRequirement requirement; 067 068 069 /** 070 * Optional language tag. 071 */ 072 private final LangTag langTag; 073 074 075 /** 076 * Optional claim value. 077 */ 078 private final String value; 079 080 081 /** 082 * Optional claim values. 083 */ 084 private final List<String> values; 085 086 087 /** 088 * Creates a new individual claim request. The claim 089 * requirement is set to voluntary (the default) and no 090 * expected value(s) are specified. 091 * 092 * @param claimName The claim name. Must not be {@code null}. 093 * @param langTag Optional language tag for the claim. 094 */ 095 public Entry(final String claimName, final LangTag langTag) { 096 097 this(claimName, ClaimRequirement.VOLUNTARY, langTag, null, null); 098 } 099 100 101 /** 102 * Creates a new individual claim request. 103 * 104 * @param claimName The claim name. Must not be {@code null}. 105 * @param requirement The claim requirement. Must not be 106 * {@code null}. 107 */ 108 public Entry(final String claimName, final ClaimRequirement requirement) { 109 110 this(claimName, requirement, null, null, null); 111 } 112 113 114 /** 115 * Creates a new individual claim request. 116 * 117 * @param claimName The claim name. Must not be {@code null}. 118 * @param requirement The claim requirement. Must not be 119 * {@code null}. 120 * @param langTag Optional language tag for the claim. 121 * @param value Optional expected value for the claim. 122 */ 123 public Entry(final String claimName, final ClaimRequirement requirement, 124 final LangTag langTag, final String value) { 125 126 this(claimName, requirement, langTag, value, null); 127 } 128 129 130 /** 131 * Creates a new individual claim request. 132 * 133 * @param claimName The claim name. Must not be {@code null}. 134 * @param requirement The claim requirement. Must not be 135 * {@code null}. 136 * @param langTag Optional language tag for the claim. 137 * @param values Optional expected values for the claim. 138 */ 139 public Entry(final String claimName, final ClaimRequirement requirement, 140 final LangTag langTag, final List<String> values) { 141 142 this(claimName, requirement, langTag, null, values); 143 } 144 145 146 /** 147 * Creates a new individual claim request. This constructor is 148 * to be used privately. Ensures that {@code value} and 149 * {@code values} are not simultaneously specified. 150 * 151 * @param claimName The claim name. Must not be {@code null}. 152 * @param requirement The claim requirement. Must not be 153 * {@code null}. 154 * @param langTag Optional language tag for the claim. 155 * @param value Optional expected value for the claim. If 156 * set, then the {@code values} parameter 157 * must not be set. 158 * @param values Optional expected values for the claim. 159 * If set, then the {@code value} parameter 160 * must not be set. 161 */ 162 private Entry(final String claimName, final ClaimRequirement requirement, final LangTag langTag, 163 final String value, final List<String> values) { 164 165 if (claimName == null) 166 throw new IllegalArgumentException("The claim name must not be null"); 167 168 this.claimName = claimName; 169 170 171 if (requirement == null) 172 throw new IllegalArgumentException("The claim requirement must not be null"); 173 174 this.requirement = requirement; 175 176 177 this.langTag = langTag; 178 179 180 if (value != null && values == null) { 181 182 this.value = value; 183 this.values = null; 184 185 } else if (value == null && values != null) { 186 187 this.value = null; 188 this.values = values; 189 190 } else if (value == null && values == null) { 191 192 this.value = null; 193 this.values = null; 194 195 } else { 196 197 throw new IllegalArgumentException("Either value or values must be specified, but not both"); 198 } 199 } 200 201 202 /** 203 * Gets the claim name. 204 * 205 * @return The claim name. 206 */ 207 public String getClaimName() { 208 209 return claimName; 210 } 211 212 213 /** 214 * Gets the claim name, optionally with the language tag 215 * appended. 216 * 217 * <p>Example with language tag: 218 * 219 * <pre> 220 * name#de-DE 221 * </pre> 222 * 223 * @param withLangTag If {@code true} the language tag will be 224 * appended to the name (if any), else not. 225 * 226 * @return The claim name, with optionally appended language 227 * tag. 228 */ 229 public String getClaimName(final boolean withLangTag) { 230 231 if (withLangTag && langTag != null) 232 return claimName + "#" + langTag.toString(); 233 else 234 return claimName; 235 } 236 237 238 /** 239 * Gets the claim requirement. 240 * 241 * @return The claim requirement. 242 */ 243 public ClaimRequirement getClaimRequirement() { 244 245 return requirement; 246 } 247 248 249 /** 250 * Gets the optional language tag for the claim. 251 * 252 * @return The language tag, {@code null} if not specified. 253 */ 254 public LangTag getLangTag() { 255 256 return langTag; 257 } 258 259 260 /** 261 * Gets the optional value for the claim. 262 * 263 * @return The value, {@code null} if not specified. 264 */ 265 public String getValue() { 266 267 return value; 268 } 269 270 271 /** 272 * Gets the optional values for the claim. 273 * 274 * @return The values, {@code null} if not specified. 275 */ 276 public List<String> getValues() { 277 278 return values; 279 } 280 281 282 /** 283 * Returns the JSON object representation of the specified 284 * collection of individual claim requests. 285 * 286 * <p>Example: 287 * 288 * <pre> 289 * { 290 * "given_name": {"essential": true}, 291 * "nickname": null, 292 * "email": {"essential": true}, 293 * "email_verified": {"essential": true}, 294 * "picture": null, 295 * "http://example.info/claims/groups": null 296 * } 297 * </pre> 298 * 299 * @param entries The entries to serialise. Must not be 300 * {@code null}. 301 * 302 * @return The corresponding JSON object, empty if no claims 303 * were found. 304 */ 305 public static JSONObject toJSONObject(final Collection<Entry> entries) { 306 307 JSONObject o = new JSONObject(); 308 309 for (Entry entry: entries) { 310 311 // Compose the optional value 312 JSONObject entrySpec = null; 313 314 if (entry.getValue() != null) { 315 316 entrySpec = new JSONObject(); 317 entrySpec.put("value", entry.getValue()); 318 } 319 320 if (entry.getValues() != null) { 321 322 // Either "value" or "values", or none 323 // may be defined 324 entrySpec = new JSONObject(); 325 entrySpec.put("values", entry.getValues()); 326 } 327 328 if (entry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) { 329 330 if (entrySpec == null) 331 entrySpec = new JSONObject(); 332 333 entrySpec.put("essential", true); 334 } 335 336 o.put(entry.getClaimName(true), entrySpec); 337 } 338 339 return o; 340 } 341 342 343 /** 344 * Parses a collection of individual claim requests from the 345 * specified JSON object. Request entries that are not 346 * understood are silently ignored. 347 */ 348 public static Collection<Entry> parseEntries(final JSONObject jsonObject) { 349 350 Collection<Entry> entries = new LinkedList<>(); 351 352 if (jsonObject.isEmpty()) 353 return entries; 354 355 for (Map.Entry<String,Object> member: jsonObject.entrySet()) { 356 357 // Process the key 358 String claimNameWithOptLangTag = member.getKey(); 359 360 String claimName; 361 LangTag langTag = null; 362 363 if (claimNameWithOptLangTag.contains("#")) { 364 365 String[] parts = claimNameWithOptLangTag.split("#", 2); 366 367 claimName = parts[0]; 368 369 try { 370 langTag = LangTag.parse(parts[1]); 371 372 } catch (LangTagException e) { 373 374 // Ignore and continue 375 continue; 376 } 377 378 } else { 379 claimName = claimNameWithOptLangTag; 380 } 381 382 // Parse the optional value 383 if (member.getValue() == null) { 384 385 // Voluntary claim with no value(s) 386 entries.add(new Entry(claimName, langTag)); 387 continue; 388 } 389 390 try { 391 JSONObject entrySpec = (JSONObject)member.getValue(); 392 393 ClaimRequirement requirement = ClaimRequirement.VOLUNTARY; 394 395 if (entrySpec.containsKey("essential")) { 396 397 boolean isEssential = (Boolean)entrySpec.get("essential"); 398 399 if (isEssential) 400 requirement = ClaimRequirement.ESSENTIAL; 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 List<String> expectedValues = new LinkedList<>(); 412 413 for (Object v: (List)entrySpec.get("values")) { 414 415 expectedValues.add((String)v); 416 } 417 418 entries.add(new Entry(claimName, requirement, langTag, expectedValues)); 419 420 } else { 421 entries.add(new Entry(claimName, requirement, langTag, (String)null)); 422 } 423 424 } catch (Exception e) { 425 // Ignore and 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<>(); 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<>(); 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<>(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<>(); 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<>(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<>(); 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<>(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<>(); 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<>(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<>(); 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("userinfo", 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 * Resolves the claims request for the specified response type and 875 * scope. The scope values that are {@link OIDCScopeValue standard 876 * OpenID Connect scope values} are resolved to their respective 877 * individual claims requests, any other scope values are ignored. 878 * 879 * @param responseType The response type. Must not be {@code null}. 880 * @param scope The scope. Must not be {@code null}. 881 * 882 * @return The claims request. 883 */ 884 public static ClaimsRequest resolve(final ResponseType responseType, final Scope scope) { 885 886 // Determine the claims target (ID token or UserInfo) 887 final boolean switchToIDToken = 888 responseType.contains(OIDCResponseTypeValue.ID_TOKEN) && 889 ! responseType.contains(ResponseType.Value.CODE) && 890 ! responseType.contains(ResponseType.Value.TOKEN); 891 892 ClaimsRequest claimsRequest = new ClaimsRequest(); 893 894 for (Scope.Value value: scope) { 895 896 Set<ClaimsRequest.Entry> entries; 897 898 if (value.equals(OIDCScopeValue.PROFILE)) { 899 900 entries = OIDCScopeValue.PROFILE.toClaimsRequestEntries(); 901 902 } else if (value.equals(OIDCScopeValue.EMAIL)) { 903 904 entries = OIDCScopeValue.EMAIL.toClaimsRequestEntries(); 905 906 } else if (value.equals(OIDCScopeValue.PHONE)) { 907 908 entries = OIDCScopeValue.PHONE.toClaimsRequestEntries(); 909 910 } else if (value.equals(OIDCScopeValue.ADDRESS)) { 911 912 entries = OIDCScopeValue.ADDRESS.toClaimsRequestEntries(); 913 914 } else { 915 916 continue; // skip 917 } 918 919 for (ClaimsRequest.Entry en: entries) { 920 921 if (switchToIDToken) 922 claimsRequest.addIDTokenClaim(en); 923 else 924 claimsRequest.addUserInfoClaim(en); 925 } 926 } 927 928 return claimsRequest; 929 } 930 931 932 /** 933 * Resolves the merged claims request from the specified OpenID Connect 934 * authorisation request parameters. The scope values that are 935 * {@link OIDCScopeValue standard OpenID Connect scope values} are 936 * resolved to their respective individual claims requests, any other 937 * scope values are ignored. 938 * 939 * @param responseType The response type. Must not be {@code null}. 940 * @param scope The scope. Must not be {@code null}. 941 * @param claimsRequest The claims request, corresponding to the 942 * optional {@code claims} OpenID Connect 943 * authorisation request parameter, {@code null} 944 * if not specified. 945 * 946 * @return The merged claims request. 947 */ 948 public static ClaimsRequest resolve(final ResponseType responseType, 949 final Scope scope, 950 final ClaimsRequest claimsRequest) { 951 952 ClaimsRequest mergedClaimsRequest = resolve(responseType, scope); 953 954 mergedClaimsRequest.add(claimsRequest); 955 956 return mergedClaimsRequest; 957 } 958 959 960 /** 961 * Resolves the merged claims request for the specified OpenID Connect 962 * authentication request. The scope values that are 963 * {@link OIDCScopeValue standard OpenID Connect scope values} are 964 * resolved to their respective individual claims requests, any other 965 * scope values are ignored. 966 * 967 * @param authRequest The OpenID Connect authentication request. Must 968 * not be {@code null}. 969 * 970 * @return The merged claims request. 971 */ 972 public static ClaimsRequest resolve(final AuthenticationRequest authRequest) { 973 974 return resolve(authRequest.getResponseType(), authRequest.getScope(), authRequest.getClaims()); 975 } 976 977 978 /** 979 * Parses a claims request from the specified JSON object 980 * representation. Unexpected members in the JSON object are silently 981 * ignored. 982 * 983 * @param jsonObject The JSON object to parse. Must not be 984 * {@code null}. 985 * 986 * @return The claims request. 987 */ 988 public static ClaimsRequest parse(final JSONObject jsonObject) { 989 990 ClaimsRequest claimsRequest = new ClaimsRequest(); 991 992 try { 993 if (jsonObject.containsKey("id_token")) { 994 995 JSONObject idTokenObject = (JSONObject)jsonObject.get("id_token"); 996 997 Collection<Entry> idTokenClaims = Entry.parseEntries(idTokenObject); 998 999 for (Entry entry: idTokenClaims) 1000 claimsRequest.addIDTokenClaim(entry); 1001 } 1002 1003 1004 if (jsonObject.containsKey("userinfo")) { 1005 1006 JSONObject userInfoObject = (JSONObject)jsonObject.get("userinfo"); 1007 1008 Collection<Entry> userInfoClaims = Entry.parseEntries(userInfoObject); 1009 1010 for (Entry entry: userInfoClaims) 1011 claimsRequest.addUserInfoClaim(entry); 1012 } 1013 1014 } catch (Exception e) { 1015 1016 // Ignore 1017 } 1018 1019 return claimsRequest; 1020 } 1021 1022 1023 /** 1024 * Parses a claims request from the specified JSON object string 1025 * representation. Unexpected members in the JSON object are silently 1026 * ignored. 1027 * 1028 * @param json The JSON object string to parse. Must not be 1029 * {@code null}. 1030 * 1031 * @return The claims request. 1032 * 1033 * @throws ParseException If the string couldn't be parsed to a valid 1034 * JSON object. 1035 */ 1036 public static ClaimsRequest parse(final String json) 1037 throws ParseException { 1038 1039 return parse(JSONObjectUtils.parse(json)); 1040 } 1041}