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; 019 020 021import java.util.*; 022 023import net.jcip.annotations.Immutable; 024import net.minidev.json.JSONAware; 025import net.minidev.json.JSONObject; 026 027import com.nimbusds.langtag.LangTag; 028import com.nimbusds.langtag.LangTagException; 029import com.nimbusds.oauth2.sdk.ParseException; 030import com.nimbusds.oauth2.sdk.ResponseType; 031import com.nimbusds.oauth2.sdk.Scope; 032import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 033import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement; 034 035 036/** 037 * Specifies the individual OpenID claims to return from the UserInfo endpoint 038 * and / or in the ID Token. 039 * 040 * <p>Related specifications: 041 * 042 * <ul> 043 * <li>OpenID Connect Core 1.0, section 5.5. 044 * <li>OpenID Connect for Identity Assurance 1.0. 045 * </ul> 046 */ 047public class ClaimsRequest implements JSONAware { 048 049 050 /** 051 * Individual OpenID claim request. 052 * 053 * <p>Related specifications: 054 * 055 * <ul> 056 * <li>OpenID Connect Core 1.0, section 5.5.1. 057 * <li>OpenID Connect for Identity Assurance 1.0. 058 * </ul> 059 */ 060 @Immutable 061 public static class Entry { 062 063 064 /** 065 * The claim name. 066 */ 067 private final String claimName; 068 069 070 /** 071 * The claim requirement. 072 */ 073 private final ClaimRequirement requirement; 074 075 076 /** 077 * Optional language tag. 078 */ 079 private final LangTag langTag; 080 081 082 /** 083 * Optional claim value. 084 */ 085 private final String value; 086 087 088 /** 089 * Optional claim values. 090 */ 091 private final List<String> values; 092 093 094 /** 095 * The claim purpose. 096 */ 097 private final String purpose; 098 099 100 /** 101 * Optional additional claim information. 102 * 103 * <p>Example additional information in the "info" member: 104 * 105 * <pre> 106 * { 107 * "userinfo" : { 108 * "email": null, 109 * "email_verified": null, 110 * "http://example.info/claims/groups" : { "info" : "custom information" } } 111 * } 112 * </pre> 113 */ 114 private final Map<String, Object> additionalInformation; 115 116 117 /** 118 * Creates a new individual claim request. The claim 119 * requirement is set to voluntary (the default) and no 120 * expected value(s) or other parameters are specified. 121 * 122 * @param claimName The claim name. Must not be {@code null}. 123 */ 124 public Entry(final String claimName) { 125 126 this(claimName, ClaimRequirement.VOLUNTARY, null, null, null, null, null); 127 } 128 129 130 /** 131 * Creates a new individual claim request. The claim 132 * requirement is set to voluntary (the default) and no 133 * expected value(s) are specified. 134 * 135 * @param claimName The claim name. Must not be {@code null}. 136 * @param langTag Optional language tag for the claim. 137 */ 138 @Deprecated 139 public Entry(final String claimName, final LangTag langTag) { 140 141 this(claimName, ClaimRequirement.VOLUNTARY, langTag, null, null); 142 } 143 144 145 /** 146 * Creates a new individual claim request. 147 * 148 * @param claimName The claim name. Must not be {@code null}. 149 * @param requirement The claim requirement. Must not be 150 * {@code null}. 151 */ 152 @Deprecated 153 public Entry(final String claimName, final ClaimRequirement requirement) { 154 155 this(claimName, requirement, null, null, null); 156 } 157 158 159 /** 160 * Creates a new individual claim request. 161 * 162 * @param claimName The claim name. Must not be {@code null}. 163 * @param requirement The claim requirement. Must not be 164 * {@code null}. 165 * @param langTag Optional language tag for the claim. 166 * @param value Optional expected value for the claim. 167 */ 168 @Deprecated 169 public Entry(final String claimName, final ClaimRequirement requirement, 170 final LangTag langTag, final String value) { 171 172 this(claimName, requirement, langTag, value, null); 173 } 174 175 176 /** 177 * Creates a new individual claim request. 178 * 179 * @param claimName The claim name. Must not be {@code null}. 180 * @param requirement The claim requirement. Must not be 181 * {@code null}. 182 * @param langTag Optional language tag for the claim. 183 * @param values Optional expected values for the claim. 184 */ 185 @Deprecated 186 public Entry(final String claimName, final ClaimRequirement requirement, 187 final LangTag langTag, final List<String> values) { 188 189 this(claimName, requirement, langTag, null, values, null, null); 190 } 191 192 193 /** 194 * Creates a new individual claim request. This constructor is 195 * to be used privately. Ensures that {@code value} and 196 * {@code values} are not simultaneously specified. 197 * 198 * @param claimName The claim name. Must not be {@code null}. 199 * @param requirement The claim requirement. Must not be 200 * {@code null}. 201 * @param langTag Optional language tag for the claim. 202 * @param value Optional expected value for the claim. If 203 * set, then the {@code values} parameter 204 * must not be set. 205 * @param values Optional expected values for the claim. 206 * If set, then the {@code value} parameter 207 * must not be set. 208 */ 209 @Deprecated 210 private Entry(final String claimName, final ClaimRequirement requirement, final LangTag langTag, 211 final String value, final List<String> values) { 212 this(claimName, requirement, langTag, value, values, null, null); 213 } 214 215 216 /** 217 * Creates a new individual claim request. This constructor is 218 * to be used privately. Ensures that {@code value} and 219 * {@code values} are not simultaneously specified. 220 * 221 * @param claimName The claim name. Must not be 222 * {@code null}. 223 * @param requirement The claim requirement. Must not 224 * be {@code null}. 225 * @param langTag Optional language tag for the 226 * claim. 227 * @param value Optional expected value for the 228 * claim. If set, then the {@code 229 * values} parameter must not be 230 * set. 231 * @param values Optional expected values for 232 * the claim. If set, then the 233 * {@code value} parameter must 234 * not be set. 235 * @param purpose The purpose for the requested 236 * claim, {@code null} if not 237 * specified. 238 * @param additionalInformation Optional additional information 239 */ 240 private Entry(final String claimName, 241 final ClaimRequirement requirement, 242 final LangTag langTag, 243 final String value, 244 final List<String> values, 245 final String purpose, 246 final Map<String, Object> additionalInformation) { 247 248 if (claimName == null) 249 throw new IllegalArgumentException("The claim name must not be null"); 250 251 this.claimName = claimName; 252 253 254 if (requirement == null) 255 throw new IllegalArgumentException("The claim requirement must not be null"); 256 257 this.requirement = requirement; 258 259 260 this.langTag = langTag; 261 262 263 if (value != null && values == null) { 264 265 this.value = value; 266 this.values = null; 267 268 } else if (value == null && values != null) { 269 270 this.value = null; 271 this.values = values; 272 273 } else if (value == null && values == null) { 274 275 this.value = null; 276 this.values = null; 277 278 } else { 279 280 throw new IllegalArgumentException("Either value or values must be specified, but not both"); 281 } 282 283 this.purpose = purpose; 284 285 this.additionalInformation = additionalInformation; 286 } 287 288 289 /** 290 * Returns the claim name. 291 * 292 * @return The claim name. 293 */ 294 public String getClaimName() { 295 296 return claimName; 297 } 298 299 300 /** 301 * Returns the claim name, optionally with the language tag 302 * appended. 303 * 304 * <p>Example with language tag: 305 * 306 * <pre> 307 * name#de-DE 308 * </pre> 309 * 310 * @param withLangTag If {@code true} the language tag will be 311 * appended to the name (if any), else not. 312 * 313 * @return The claim name, with optionally appended language 314 * tag. 315 */ 316 public String getClaimName(final boolean withLangTag) { 317 318 if (withLangTag && langTag != null) 319 return claimName + "#" + langTag.toString(); 320 else 321 return claimName; 322 } 323 324 325 /** 326 * Returns a new claim entry with the specified requirement. 327 * 328 * @param requirement The claim requirement. 329 * 330 * @return The new entry. 331 */ 332 public Entry withClaimRequirement(final ClaimRequirement requirement) { 333 334 return new Entry(claimName, requirement, langTag, value, values, purpose, additionalInformation); 335 } 336 337 338 /** 339 * Returns the claim requirement. 340 * 341 * @return The claim requirement. 342 */ 343 public ClaimRequirement getClaimRequirement() { 344 345 return requirement; 346 } 347 348 349 /** 350 * Returns a new claim entry with the specified language tag 351 * for the claim. 352 * 353 * @param langTag The language tag, {@code null} if not 354 * specified. 355 * 356 * @return The new entry. 357 */ 358 public Entry withLangTag(final LangTag langTag) { 359 360 return new Entry(claimName, requirement, langTag, value, values, purpose, additionalInformation); 361 } 362 363 364 /** 365 * Returns the optional language tag for the claim. 366 * 367 * @return The language tag, {@code null} if not specified. 368 */ 369 public LangTag getLangTag() { 370 371 return langTag; 372 } 373 374 375 /** 376 * Returns a new claim entry with the specified requested value 377 * for the claim. 378 * 379 * @param value The value, {@code null} if not specified. 380 * 381 * @return The new entry. 382 */ 383 public Entry withValue(final String value) { 384 385 return new Entry(claimName, requirement, langTag, value, null, purpose, additionalInformation); 386 } 387 388 389 /** 390 * Returns the requested value for the claim. 391 * 392 * @return The value, {@code null} if not specified. 393 */ 394 public String getValue() { 395 396 return value; 397 } 398 399 400 /** 401 * Returns a new claim entry with the specified requested 402 * values for the claim. 403 * 404 * @param values The values, {@code null} if not specified. 405 * 406 * @return The new entry. 407 */ 408 public Entry withValues(final List<String> values) { 409 410 return new Entry(claimName, requirement, langTag, null, values, purpose, additionalInformation); 411 } 412 413 414 /** 415 * Returns the optional values for the claim. 416 * 417 * @return The values, {@code null} if not specified. 418 */ 419 public List<String> getValues() { 420 421 return values; 422 } 423 424 425 /** 426 * Returns a new claim entry with the specified purpose for the 427 * requested claim. 428 * 429 * @param purpose The purpose, {@code null} if not specified. 430 * 431 * @return The new entry. 432 */ 433 public Entry withPurpose(final String purpose) { 434 435 return new Entry(claimName, requirement, langTag, value, values, purpose, additionalInformation); 436 } 437 438 439 /** 440 * Returns the optional purpose for the requested claim. 441 * 442 * @return The purpose, {@code null} if not specified. 443 */ 444 public String getPurpose() { 445 446 return purpose; 447 } 448 449 450 /** 451 * Returns a new claim entry with the specified additional 452 * information for the claim. 453 * 454 * <p>Example additional information in the "info" member: 455 * 456 * <pre> 457 * { 458 * "userinfo" : { 459 * "email": null, 460 * "email_verified": null, 461 * "http://example.info/claims/groups" : { "info" : "custom information" } } 462 * } 463 * </pre> 464 * 465 * @param additionalInformation The additional information, 466 * {@code null} if not specified. 467 * 468 * @return The new entry. 469 */ 470 public Entry withAdditionalInformation(final Map<String, Object> additionalInformation) { 471 472 return new Entry(claimName, requirement, langTag, value, values, purpose, additionalInformation); 473 } 474 475 476 /** 477 * Returns the additional information for the claim. 478 * 479 * <p>Example additional information in the "info" member: 480 * 481 * <pre> 482 * { 483 * "userinfo" : { 484 * "email": null, 485 * "email_verified": null, 486 * "http://example.info/claims/groups" : { "info" : "custom information" } } 487 * } 488 * </pre> 489 * 490 * @return The additional information, {@code null} if not 491 * specified. 492 */ 493 public Map<String, Object> getAdditionalInformation() { 494 return additionalInformation; 495 } 496 497 498 /** 499 * Returns the JSON object representation of the specified 500 * collection of individual claim requests. 501 * 502 * <p>Example: 503 * 504 * <pre> 505 * { 506 * "given_name": {"essential": true}, 507 * "nickname": null, 508 * "email": {"essential": true}, 509 * "email_verified": {"essential": true}, 510 * "picture": null, 511 * "http://example.info/claims/groups": null 512 * } 513 * </pre> 514 * 515 * @param entries The entries to serialise. Must not be 516 * {@code null}. 517 * @return The corresponding JSON object, empty if no claims 518 * were found. 519 */ 520 public static JSONObject toJSONObject(final Collection<Entry> entries) { 521 522 JSONObject o = new JSONObject(); 523 524 for (Entry entry : entries) { 525 526 // Compose the optional value 527 JSONObject entrySpec = null; 528 529 if (entry.getValue() != null) { 530 531 entrySpec = new JSONObject(); 532 entrySpec.put("value", entry.getValue()); 533 } 534 535 if (entry.getValues() != null) { 536 537 // Either "value" or "values", or none 538 // may be defined 539 entrySpec = new JSONObject(); 540 entrySpec.put("values", entry.getValues()); 541 } 542 543 if (entry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) { 544 545 if (entrySpec == null) 546 entrySpec = new JSONObject(); 547 548 entrySpec.put("essential", true); 549 } 550 551 if (entry.getPurpose() != null) { 552 if (entrySpec == null) { 553 entrySpec = new JSONObject(); 554 } 555 entrySpec.put("purpose", entry.getPurpose()); 556 } 557 558 if (entry.getAdditionalInformation() != null) { 559 if (entrySpec == null) { 560 entrySpec = new JSONObject(); 561 } 562 for (Map.Entry<String, Object> additionalInformationEntry : entry.getAdditionalInformation().entrySet()) { 563 entrySpec.put(additionalInformationEntry.getKey(), additionalInformationEntry.getValue()); 564 } 565 } 566 567 o.put(entry.getClaimName(true), entrySpec); 568 } 569 570 return o; 571 } 572 573 574 /** 575 * Parses a collection of individual claim requests from the 576 * specified JSON object. Request entries that are not 577 * understood are silently ignored. 578 * 579 * @param jsonObject The JSON object to parse. Must not be 580 * {@code null}. 581 * 582 * @return The collection of claim requests. 583 */ 584 public static Collection<Entry> parseEntries(final JSONObject jsonObject) { 585 586 Collection<Entry> entries = new LinkedList<>(); 587 588 if (jsonObject.isEmpty()) 589 return entries; 590 591 for (Map.Entry<String, Object> member : jsonObject.entrySet()) { 592 593 // Process the key 594 String claimNameWithOptLangTag = member.getKey(); 595 596 String claimName; 597 LangTag langTag = null; 598 599 if (claimNameWithOptLangTag.contains("#")) { 600 601 String[] parts = claimNameWithOptLangTag.split("#", 2); 602 603 claimName = parts[0]; 604 605 try { 606 langTag = LangTag.parse(parts[1]); 607 608 } catch (LangTagException e) { 609 610 // Ignore and continue 611 continue; 612 } 613 614 } else { 615 claimName = claimNameWithOptLangTag; 616 } 617 618 // Parse the optional value 619 if (member.getValue() == null) { 620 621 // Voluntary claim with no value(s) 622 entries.add(new Entry(claimName, langTag)); 623 continue; 624 } 625 626 try { 627 JSONObject entrySpec = (JSONObject) member.getValue(); 628 629 ClaimRequirement requirement = ClaimRequirement.VOLUNTARY; 630 631 if (entrySpec.containsKey("essential")) { 632 633 boolean isEssential = (Boolean) entrySpec.get("essential"); 634 635 if (isEssential) 636 requirement = ClaimRequirement.ESSENTIAL; 637 } 638 639 String purpose = null; 640 if (entrySpec.containsKey("purpose")) { 641 purpose = (String) entrySpec.get("purpose"); 642 } 643 644 if (entrySpec.containsKey("value")) { 645 646 String expectedValue = (String) entrySpec.get("value"); 647 Map<String, Object> additionalInformation = getAdditionalInformationFromClaim(entrySpec); 648 entries.add(new Entry(claimName, requirement, langTag, expectedValue, null, purpose, additionalInformation)); 649 650 } else if (entrySpec.containsKey("values")) { 651 652 List<String> expectedValues = new LinkedList<>(); 653 654 for (Object v : (List) entrySpec.get("values")) { 655 656 expectedValues.add((String) v); 657 } 658 Map<String, Object> additionalInformation = getAdditionalInformationFromClaim(entrySpec); 659 660 entries.add(new Entry(claimName, requirement, langTag, null, expectedValues, purpose, additionalInformation)); 661 662 } else { 663 Map<String, Object> additionalInformation = getAdditionalInformationFromClaim(entrySpec); 664 entries.add(new Entry(claimName, requirement, langTag, (String) null, null, purpose, additionalInformation)); 665 } 666 667 } catch (Exception e) { 668 // Ignore and continue 669 } 670 } 671 672 return entries; 673 } 674 675 676 private static Map<String, Object> getAdditionalInformationFromClaim(JSONObject entrySpec) { 677 List<String> keysToRemove = Arrays.asList("essential", "value", "values", "purpose"); 678 entrySpec.keySet().removeAll(keysToRemove); 679 Map<String, Object> additionalClaimInformation = new HashMap<>(); 680 for (Map.Entry<String, Object> additionalClaimInformationEntry : entrySpec.entrySet()) { 681 additionalClaimInformation.put(additionalClaimInformationEntry.getKey(), additionalClaimInformationEntry.getValue()); 682 } 683 return additionalClaimInformation.isEmpty() ? null : additionalClaimInformation; 684 } 685 } 686 687 688 /** 689 * The requested ID token claims, keyed by claim name and language tag. 690 */ 691 private final Map<Map.Entry<String, LangTag>, Entry> idTokenClaims = new HashMap<>(); 692 693 694 /** 695 * The requested verified ID token claims, keyed by claim name and 696 * language tag. 697 */ 698 private final Map<Map.Entry<String, LangTag>, Entry> verifiedIDTokenClaims = new HashMap<>(); 699 700 701 /** 702 * The verification element for the requested verified ID token claims. 703 */ 704 private JSONObject idTokenClaimsVerification; 705 706 707 /** 708 * The requested UserInfo claims, keyed by claim name and language tag. 709 */ 710 private final Map<Map.Entry<String, LangTag>, Entry> userInfoClaims = new HashMap<>(); 711 712 713 /** 714 * The requested verified UserInfo claims, keyed by claim name and 715 * language tag. 716 */ 717 private final Map<Map.Entry<String, LangTag>, Entry> verifiedUserInfoClaims = new HashMap<>(); 718 719 720 /** 721 * The verification element for the requested verified UserInfo claims. 722 */ 723 private JSONObject userInfoClaimsVerification; 724 725 726 /** 727 * Creates a new empty claims request. 728 */ 729 public ClaimsRequest() { 730 731 // Nothing to initialise 732 } 733 734 735 /** 736 * Adds the entries from the specified other claims request. 737 * 738 * @param other The other claims request. If {@code null} no claims 739 * request entries will be added to this claims request. 740 */ 741 public void add(final ClaimsRequest other) { 742 743 if (other == null) 744 return; 745 746 idTokenClaims.putAll(other.idTokenClaims); 747 verifiedIDTokenClaims.putAll(other.verifiedIDTokenClaims); 748 idTokenClaimsVerification = other.idTokenClaimsVerification; 749 750 userInfoClaims.putAll(other.userInfoClaims); 751 verifiedUserInfoClaims.putAll(other.verifiedUserInfoClaims); 752 userInfoClaimsVerification = other.userInfoClaimsVerification; 753 } 754 755 756 /** 757 * Adds the specified ID token claim to the request. It is marked as 758 * voluntary and no language tag and value(s) are associated with it. 759 * 760 * @param claimName The claim name. Must not be {@code null}. 761 */ 762 public void addIDTokenClaim(final String claimName) { 763 764 addIDTokenClaim(claimName, ClaimRequirement.VOLUNTARY); 765 } 766 767 768 /** 769 * Adds the specified ID token claim to the request. No language tag 770 * and value(s) are associated with it. 771 * 772 * @param claimName The claim name. Must not be {@code null}. 773 * @param requirement The claim requirement. Must not be {@code null}. 774 */ 775 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement) { 776 777 addIDTokenClaim(claimName, requirement, null); 778 } 779 780 781 /** 782 * Adds the specified ID token claim to the request. No value(s) are 783 * associated with it. 784 * 785 * @param claimName The claim name. Must not be {@code null}. 786 * @param requirement The claim requirement. Must not be {@code null}. 787 * @param langTag The associated language tag, {@code null} if not 788 * specified. 789 */ 790 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 791 final LangTag langTag) { 792 793 addIDTokenClaim(claimName, requirement, langTag, (String) null); 794 } 795 796 797 /** 798 * Adds the specified ID token claim to the request. 799 * 800 * @param claimName The claim name. Must not be {@code null}. 801 * @param requirement The claim requirement. Must not be {@code null}. 802 * @param langTag The associated language tag, {@code null} if not 803 * specified. 804 * @param value The expected claim value, {@code null} if not 805 * specified. 806 */ 807 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 808 final LangTag langTag, final String value) { 809 810 addIDTokenClaim(new Entry(claimName, requirement, langTag, value)); 811 } 812 813 814 /** 815 * Adds the specified ID token claim to the request. 816 * 817 * @param claimName The claim name. Must not be 818 * {@code null}. 819 * @param requirement The claim requirement. Must not be 820 * {@code null}. 821 * @param langTag The associated language tag, 822 * {@code null} if not specified. 823 * @param value The expected claim value, {@code null} 824 * if not specified. 825 * @param additionalInformation The additional information for this 826 * claim, {@code null} if not specified. 827 */ 828 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 829 final LangTag langTag, final String value, final Map<String, Object> additionalInformation) { 830 831 addIDTokenClaim(new Entry(claimName, requirement, langTag, value, null, null, additionalInformation)); 832 } 833 834 835 /** 836 * Adds the specified ID token claim to the request. 837 * 838 * @param claimName The claim name. Must not be {@code null}. 839 * @param requirement The claim requirement. Must not be {@code null}. 840 * @param langTag The associated language tag, {@code null} if not 841 * specified. 842 * @param values The expected claim values, {@code null} if not 843 * specified. 844 */ 845 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 846 final LangTag langTag, final List<String> values) { 847 848 addIDTokenClaim(new Entry(claimName, requirement, langTag, values)); 849 } 850 851 852 /** 853 * Adds the specified ID token claim to the request. 854 * 855 * @param claimName The claim name. Must not be 856 * {@code null}. 857 * @param requirement The claim requirement. Must not be 858 * {@code null}. 859 * @param langTag The associated language tag, 860 * {@code null} if not specified. 861 * @param values The expected claim values, {@code null} 862 * if not specified. 863 * @param additionalInformation The additional information for this 864 * claim, {@code null} if not specified. 865 */ 866 public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 867 final LangTag langTag, final List<String> values, final Map<String, Object> additionalInformation) { 868 869 addIDTokenClaim(new Entry(claimName, requirement, langTag, null, values, null, additionalInformation)); 870 } 871 872 873 private static Map.Entry<String, LangTag> toKey(final Entry entry) { 874 875 return new AbstractMap.SimpleImmutableEntry<>( 876 entry.getClaimName(), 877 entry.getLangTag()); 878 } 879 880 881 /** 882 * Adds the specified ID token claim to the request. 883 * 884 * @param entry The individual ID token claim request. Must not be 885 * {@code null}. 886 */ 887 public void addIDTokenClaim(final Entry entry) { 888 889 idTokenClaims.put(toKey(entry), entry); 890 } 891 892 893 /** 894 * Adds the specified verified ID token claim to the request. 895 * 896 * @param entry The individual verified ID token claim request. Must 897 * not be {@code null}. 898 */ 899 public void addVerifiedIDTokenClaim(final Entry entry) { 900 901 verifiedIDTokenClaims.put(toKey(entry), entry); 902 } 903 904 905 /** 906 * Sets the {@code verification} element for the requested verified ID 907 * token claims. 908 * 909 * @param jsonObject The {@code verification} JSON object, {@code null} 910 * if not specified. 911 */ 912 public void setIDTokenClaimsVerificationJSONObject(final JSONObject jsonObject) { 913 914 this.idTokenClaimsVerification = jsonObject; 915 } 916 917 918 /** 919 * Gets the {@code verification} element for the requested verified ID 920 * token claims. 921 * 922 * @return The {@code verification} JSON object, {@code null} if not 923 * specified. 924 */ 925 public JSONObject getIDTokenClaimsVerificationJSONObject() { 926 927 return idTokenClaimsVerification; 928 } 929 930 931 /** 932 * Gets the requested ID token claims. 933 * 934 * @return The ID token claims, as an unmodifiable collection, empty 935 * set if none. 936 */ 937 public Collection<Entry> getIDTokenClaims() { 938 939 return Collections.unmodifiableCollection(idTokenClaims.values()); 940 } 941 942 943 /** 944 * Gets the requested verified ID token claims. 945 * 946 * @return The verified ID token claims, as an unmodifiable collection, 947 * empty set if none. 948 */ 949 public Collection<Entry> getVerifiedIDTokenClaims() { 950 951 return Collections.unmodifiableCollection(verifiedIDTokenClaims.values()); 952 } 953 954 955 private static Set<String> getClaimNames(final Map<Map.Entry<String, LangTag>, Entry> claims, final boolean withLangTag) { 956 957 Set<String> names = new HashSet<>(); 958 959 for (Entry en : claims.values()) 960 names.add(en.getClaimName(withLangTag)); 961 962 return Collections.unmodifiableSet(names); 963 } 964 965 966 /** 967 * Gets the names of the requested ID token claim names. 968 * 969 * @param withLangTag If {@code true} the language tags, if any, will 970 * be appended to the names, else not. 971 * 972 * @return The ID token claim names, as an unmodifiable set, empty set 973 * if none. 974 */ 975 public Set<String> getIDTokenClaimNames(final boolean withLangTag) { 976 977 return getClaimNames(idTokenClaims, withLangTag); 978 } 979 980 981 /** 982 * Gets the names of the requested verified ID token claim names. 983 * 984 * @param withLangTag If {@code true} the language tags, if any, will 985 * be appended to the names, else not. 986 * 987 * @return The ID token claim names, as an unmodifiable set, empty set 988 * if none. 989 */ 990 public Set<String> getVerifiedIDTokenClaimNames(final boolean withLangTag) { 991 992 return getClaimNames(verifiedIDTokenClaims, withLangTag); 993 } 994 995 996 private static Map.Entry<String, LangTag> toKey(final String claimName, final LangTag langTag) { 997 998 return new AbstractMap.SimpleImmutableEntry<>(claimName, langTag); 999 } 1000 1001 1002 /** 1003 * Removes the specified ID token claim from the request. 1004 * 1005 * @param claimName The claim name. Must not be {@code null}. 1006 * @param langTag The associated language tag, {@code null} if none. 1007 * 1008 * @return The removed ID token claim, {@code null} if not found. 1009 */ 1010 public Entry removeIDTokenClaim(final String claimName, final LangTag langTag) { 1011 1012 return idTokenClaims.remove(toKey(claimName, langTag)); 1013 } 1014 1015 1016 /** 1017 * Removes the specified verified ID token claim from the request. 1018 * 1019 * @param claimName The claim name. Must not be {@code null}. 1020 * @param langTag The associated language tag, {@code null} if none. 1021 * 1022 * @return The removed ID token claim, {@code null} if not found. 1023 */ 1024 public Entry removeVerifiedIDTokenClaim(final String claimName, final LangTag langTag) { 1025 1026 return verifiedIDTokenClaims.remove(toKey(claimName, langTag)); 1027 } 1028 1029 1030 private static Collection<Entry> removeClaims(final Map<Map.Entry<String, LangTag>, Entry> claims, final String claimName) { 1031 1032 Collection<Entry> removedClaims = new LinkedList<>(); 1033 1034 Iterator<Map.Entry<Map.Entry<String, LangTag>, Entry>> it = claims.entrySet().iterator(); 1035 1036 while (it.hasNext()) { 1037 1038 Map.Entry<Map.Entry<String, LangTag>, Entry> reqEntry = it.next(); 1039 1040 if (reqEntry.getKey().getKey().equals(claimName)) { 1041 1042 removedClaims.add(reqEntry.getValue()); 1043 1044 it.remove(); 1045 } 1046 } 1047 1048 return Collections.unmodifiableCollection(removedClaims); 1049 } 1050 1051 1052 /** 1053 * Removes the specified ID token claims from the request, in all 1054 * existing language tag variations. 1055 * 1056 * @param claimName The claim name. Must not be {@code null}. 1057 * 1058 * @return The removed ID token claims, as an unmodifiable collection, 1059 * empty set if none were found. 1060 */ 1061 public Collection<Entry> removeIDTokenClaims(final String claimName) { 1062 1063 return removeClaims(idTokenClaims, claimName); 1064 } 1065 1066 1067 /** 1068 * Removes the specified verified ID token claims from the request, in 1069 * all existing language tag variations. 1070 * 1071 * @param claimName The claim name. Must not be {@code null}. 1072 * 1073 * @return The removed ID token claims, as an unmodifiable collection, 1074 * empty set if none were found. 1075 */ 1076 public Collection<Entry> removeVerifiedIDTokenClaims(final String claimName) { 1077 1078 return removeClaims(verifiedIDTokenClaims, claimName); 1079 } 1080 1081 1082 /** 1083 * Adds the specified UserInfo claim to the request. It is marked as 1084 * voluntary and no language tag and value(s) are associated with it. 1085 * 1086 * @param claimName The claim name. Must not be {@code null}. 1087 */ 1088 public void addUserInfoClaim(final String claimName) { 1089 1090 addUserInfoClaim(claimName, ClaimRequirement.VOLUNTARY); 1091 } 1092 1093 1094 /** 1095 * Adds the specified UserInfo claim to the request. No language tag and 1096 * value(s) are associated with it. 1097 * 1098 * @param claimName The claim name. Must not be {@code null}. 1099 * @param requirement The claim requirement. Must not be {@code null}. 1100 */ 1101 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement) { 1102 1103 addUserInfoClaim(claimName, requirement, null); 1104 } 1105 1106 1107 /** 1108 * Adds the specified UserInfo claim to the request. No value(s) are 1109 * associated with it. 1110 * 1111 * @param claimName The claim name. Must not be {@code null}. 1112 * @param requirement The claim requirement. Must not be {@code null}. 1113 * @param langTag The associated language tag, {@code null} if not 1114 * specified. 1115 */ 1116 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 1117 final LangTag langTag) { 1118 1119 1120 addUserInfoClaim(claimName, requirement, langTag, (String) null); 1121 } 1122 1123 1124 /** 1125 * Adds the specified UserInfo claim to the request. 1126 * 1127 * @param claimName The claim name. Must not be {@code null}. 1128 * @param requirement The claim requirement. Must not be {@code null}. 1129 * @param langTag The associated language tag, {@code null} if not 1130 * specified. 1131 * @param value The expected claim value, {@code null} if not 1132 * specified. 1133 */ 1134 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 1135 final LangTag langTag, final String value) { 1136 1137 addUserInfoClaim(new Entry(claimName, requirement, langTag, value)); 1138 } 1139 1140 1141 /** 1142 * Adds the specified UserInfo claim to the request. 1143 * 1144 * @param claimName The claim name. Must not be {@code 1145 * null}. 1146 * @param requirement The claim requirement. Must not be 1147 * {@code null}. 1148 * @param langTag The associated language tag, {@code 1149 * null} if not specified. 1150 * @param value The expected claim value, {@code null} 1151 * if not specified. 1152 * @param additionalInformation The additional information for this 1153 * claim, {@code null} if not specified. 1154 */ 1155 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 1156 final LangTag langTag, final String value, final Map<String, Object> additionalInformation) { 1157 1158 addUserInfoClaim(new Entry(claimName, requirement, langTag, value, null, null, additionalInformation)); 1159 } 1160 1161 1162 /** 1163 * Adds the specified UserInfo claim to the request. 1164 * 1165 * @param claimName The claim name. Must not be {@code null}. 1166 * @param requirement The claim requirement. Must not be {@code null}. 1167 * @param langTag The associated language tag, {@code null} if not 1168 * specified. 1169 * @param values The expected claim values, {@code null} if not 1170 * specified. 1171 */ 1172 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 1173 final LangTag langTag, final List<String> values) { 1174 1175 addUserInfoClaim(new Entry(claimName, requirement, langTag, values)); 1176 } 1177 1178 1179 /** 1180 * Adds the specified UserInfo claim to the request. 1181 * 1182 * @param claimName The claim name. Must not be 1183 * {@code null}. 1184 * @param requirement The claim requirement. Must not be 1185 * {@code null}. 1186 * @param langTag The associated language tag, 1187 * {@code null} if not specified. 1188 * @param values The expected claim values, {@code null} 1189 * if not specified. 1190 * @param additionalInformation The additional information for this 1191 * claim, {@code null} if not specified. 1192 */ 1193 public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 1194 final LangTag langTag, final List<String> values, final Map<String, Object> additionalInformation) { 1195 1196 addUserInfoClaim(new Entry(claimName, requirement, langTag, null, values, null, additionalInformation)); 1197 } 1198 1199 1200 /** 1201 * Adds the specified UserInfo claim to the request. 1202 * 1203 * @param entry The individual UserInfo claim request. Must not be 1204 * {@code null}. 1205 */ 1206 public void addUserInfoClaim(final Entry entry) { 1207 1208 userInfoClaims.put(toKey(entry), entry); 1209 } 1210 1211 1212 /** 1213 * Adds the specified verified UserInfo claim to the request. 1214 * 1215 * @param entry The individual verified UserInfo claim request. Must 1216 * not be {@code null}. 1217 */ 1218 public void addVerifiedUserInfoClaim(final Entry entry) { 1219 1220 verifiedUserInfoClaims.put(toKey(entry), entry); 1221 } 1222 1223 1224 /** 1225 * Sets the {@code verification} element for the requested verified 1226 * UserInfo claims. 1227 * 1228 * @param jsonObject The {@code verification} JSON object, {@code null} 1229 * if not specified. 1230 */ 1231 public void setUserInfoClaimsVerificationJSONObject(final JSONObject jsonObject) { 1232 1233 this.userInfoClaimsVerification = jsonObject; 1234 } 1235 1236 1237 /** 1238 * Gets the {@code verification} element for the requested verified 1239 * UserInfo claims. 1240 * 1241 * @return The {@code verification} JSON object, {@code null} if not 1242 * specified. 1243 */ 1244 public JSONObject getUserInfoClaimsVerificationJSONObject() { 1245 1246 return userInfoClaimsVerification; 1247 } 1248 1249 1250 /** 1251 * Gets the requested UserInfo claims. 1252 * 1253 * @return The UserInfo claims, as an unmodifiable collection, empty 1254 * set if none. 1255 */ 1256 public Collection<Entry> getUserInfoClaims() { 1257 1258 return Collections.unmodifiableCollection(userInfoClaims.values()); 1259 } 1260 1261 1262 /** 1263 * Gets the requested verified UserInfo claims. 1264 * 1265 * @return The UserInfo claims, as an unmodifiable collection, empty 1266 * set if none. 1267 */ 1268 public Collection<Entry> getVerifiedUserInfoClaims() { 1269 1270 return Collections.unmodifiableCollection(verifiedUserInfoClaims.values()); 1271 } 1272 1273 1274 /** 1275 * Gets the names of the requested UserInfo claim names. 1276 * 1277 * @param withLangTag If {@code true} the language tags, if any, will 1278 * be appended to the names, else not. 1279 * 1280 * @return The UserInfo claim names, as an unmodifiable set, empty set 1281 * if none. 1282 */ 1283 public Set<String> getUserInfoClaimNames(final boolean withLangTag) { 1284 1285 return getClaimNames(userInfoClaims, withLangTag); 1286 } 1287 1288 1289 /** 1290 * Gets the names of the requested verified UserInfo claim names. 1291 * 1292 * @param withLangTag If {@code true} the language tags, if any, will 1293 * be appended to the names, else not. 1294 * 1295 * @return The UserInfo claim names, as an unmodifiable set, empty set 1296 * if none. 1297 */ 1298 public Set<String> getVerifiedUserInfoClaimNames(final boolean withLangTag) { 1299 1300 return getClaimNames(verifiedUserInfoClaims, withLangTag); 1301 } 1302 1303 1304 /** 1305 * Removes the specified UserInfo claim from the request. 1306 * 1307 * @param claimName The claim name. Must not be {@code null}. 1308 * @param langTag The associated language tag, {@code null} if none. 1309 * 1310 * @return The removed UserInfo claim, {@code null} if not found. 1311 */ 1312 public Entry removeUserInfoClaim(final String claimName, final LangTag langTag) { 1313 1314 return userInfoClaims.remove(toKey(claimName, langTag)); 1315 } 1316 1317 1318 /** 1319 * Removes the specified verified UserInfo claim from the request. 1320 * 1321 * @param claimName The claim name. Must not be {@code null}. 1322 * @param langTag The associated language tag, {@code null} if none. 1323 * 1324 * @return The removed UserInfo claim, {@code null} if not found. 1325 */ 1326 public Entry removeVerifiedUserInfoClaim(final String claimName, final LangTag langTag) { 1327 1328 return verifiedUserInfoClaims.remove(toKey(claimName, langTag)); 1329 } 1330 1331 1332 /** 1333 * Removes the specified UserInfo claims from the request, in all 1334 * existing language tag variations. 1335 * 1336 * @param claimName The claim name. Must not be {@code null}. 1337 * 1338 * @return The removed UserInfo claims, as an unmodifiable collection, 1339 * empty set if none were found. 1340 */ 1341 public Collection<Entry> removeUserInfoClaims(final String claimName) { 1342 1343 return removeClaims(userInfoClaims, claimName); 1344 } 1345 1346 1347 /** 1348 * Removes the specified verified UserInfo claims from the request, in 1349 * all existing language tag variations. 1350 * 1351 * @param claimName The claim name. Must not be {@code null}. 1352 * 1353 * @return The removed UserInfo claims, as an unmodifiable collection, 1354 * empty set if none were found. 1355 */ 1356 public Collection<Entry> removeVerifiedUserInfoClaims(final String claimName) { 1357 1358 return removeClaims(verifiedUserInfoClaims, claimName); 1359 } 1360 1361 1362 /** 1363 * Returns the JSON object representation of this claims request. 1364 * 1365 * <p>Example: 1366 * 1367 * <pre> 1368 * { 1369 * "userinfo": 1370 * { 1371 * "given_name": {"essential": true}, 1372 * "nickname": null, 1373 * "email": {"essential": true}, 1374 * "email_verified": {"essential": true}, 1375 * "picture": null, 1376 * "http://example.info/claims/groups": null 1377 * }, 1378 * "id_token": 1379 * { 1380 * "auth_time": {"essential": true}, 1381 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 1382 * } 1383 * } 1384 * </pre> 1385 * 1386 * @return The corresponding JSON object, empty if no ID token and 1387 * UserInfo claims are specified. 1388 */ 1389 public JSONObject toJSONObject() { 1390 1391 JSONObject o = new JSONObject(); 1392 1393 if (! getIDTokenClaims().isEmpty()) { 1394 1395 o.put("id_token", Entry.toJSONObject(getIDTokenClaims())); 1396 } 1397 1398 if (! getVerifiedIDTokenClaims().isEmpty()) { 1399 1400 JSONObject idTokenObject; 1401 if (o.get("id_token") != null) { 1402 idTokenObject = (JSONObject) o.get("id_token"); 1403 } else { 1404 idTokenObject = new JSONObject(); 1405 } 1406 1407 JSONObject verifiedClaims = new JSONObject(); 1408 1409 verifiedClaims.put("claims", Entry.toJSONObject(getVerifiedIDTokenClaims())); 1410 1411 if (getIDTokenClaimsVerificationJSONObject() != null) { 1412 verifiedClaims.put("verification", getIDTokenClaimsVerificationJSONObject()); 1413 } 1414 1415 idTokenObject.put("verified_claims", verifiedClaims); 1416 o.put("id_token", idTokenObject); 1417 } 1418 1419 if (! getUserInfoClaims().isEmpty()) { 1420 1421 o.put("userinfo", Entry.toJSONObject(getUserInfoClaims())); 1422 } 1423 1424 if (! getVerifiedUserInfoClaims().isEmpty()) { 1425 1426 JSONObject userInfoObject; 1427 if (o.get("userinfo") != null) { 1428 userInfoObject = (JSONObject) o.get("userinfo"); 1429 } else { 1430 userInfoObject = new JSONObject(); 1431 } 1432 1433 JSONObject verifiedClaims = new JSONObject(); 1434 1435 verifiedClaims.put("claims", Entry.toJSONObject(getVerifiedUserInfoClaims())); 1436 1437 if (getUserInfoClaimsVerificationJSONObject() != null) { 1438 verifiedClaims.put("verification", getUserInfoClaimsVerificationJSONObject()); 1439 } 1440 1441 userInfoObject.put("verified_claims", verifiedClaims); 1442 o.put("userinfo", userInfoObject); 1443 } 1444 1445 return o; 1446 } 1447 1448 1449 @Override 1450 public String toJSONString() { 1451 return toJSONObject().toJSONString(); 1452 } 1453 1454 1455 @Override 1456 public String toString() { 1457 1458 return toJSONString(); 1459 } 1460 1461 1462 /** 1463 * Resolves the claims request for the specified response type and 1464 * scope. The scope values that are {@link OIDCScopeValue standard 1465 * OpenID scope values} are resolved to their respective individual 1466 * claims requests, any other scope values are ignored. 1467 * 1468 * @param responseType The response type. Must not be {@code null}. 1469 * @param scope The scope, {@code null} if not specified (for a 1470 * plain OAuth 2.0 authorisation request with no 1471 * scope explicitly specified). 1472 * 1473 * @return The claims request. 1474 */ 1475 public static ClaimsRequest resolve(final ResponseType responseType, final Scope scope) { 1476 1477 return resolve(responseType, scope, Collections.<Scope.Value, Set<String>>emptyMap()); 1478 } 1479 1480 1481 /** 1482 * Resolves the claims request for the specified response type and 1483 * scope. The scope values that are {@link OIDCScopeValue standard 1484 * OpenID scope values} are resolved to their respective individual 1485 * claims requests, any other scope values are checked in the specified 1486 * custom claims map and resolved accordingly. 1487 * 1488 * @param responseType The response type. Must not be {@code null}. 1489 * @param scope The scope, {@code null} if not specified (for a 1490 * plain OAuth 2.0 authorisation request with no 1491 * scope explicitly specified). 1492 * @param customClaims Custom scope value to set of claim names map, 1493 * {@code null} if not specified. 1494 * 1495 * @return The claims request. 1496 */ 1497 public static ClaimsRequest resolve(final ResponseType responseType, 1498 final Scope scope, 1499 final Map<Scope.Value, Set<String>> customClaims) { 1500 1501 // Determine the claims target (ID token or UserInfo) 1502 final boolean switchToIDToken = 1503 responseType.contains(OIDCResponseTypeValue.ID_TOKEN) && 1504 !responseType.contains(ResponseType.Value.CODE) && 1505 !responseType.contains(ResponseType.Value.TOKEN); 1506 1507 ClaimsRequest claimsRequest = new ClaimsRequest(); 1508 1509 if (scope == null) { 1510 // Plain OAuth 2.0 mode 1511 return claimsRequest; 1512 } 1513 1514 for (Scope.Value value : scope) { 1515 1516 Set<ClaimsRequest.Entry> entries; 1517 1518 if (value.equals(OIDCScopeValue.PROFILE)) { 1519 1520 entries = OIDCScopeValue.PROFILE.toClaimsRequestEntries(); 1521 1522 } else if (value.equals(OIDCScopeValue.EMAIL)) { 1523 1524 entries = OIDCScopeValue.EMAIL.toClaimsRequestEntries(); 1525 1526 } else if (value.equals(OIDCScopeValue.PHONE)) { 1527 1528 entries = OIDCScopeValue.PHONE.toClaimsRequestEntries(); 1529 1530 } else if (value.equals(OIDCScopeValue.ADDRESS)) { 1531 1532 entries = OIDCScopeValue.ADDRESS.toClaimsRequestEntries(); 1533 1534 } else if (customClaims != null && customClaims.containsKey(value)) { 1535 1536 // Process custom scope value -> claim names expansion, e.g. 1537 // "corp_profile" -> ["employeeNumber", "dept", "ext"] 1538 Set<String> claimNames = customClaims.get(value); 1539 1540 if (claimNames == null || claimNames.isEmpty()) { 1541 continue; // skip 1542 } 1543 1544 entries = new HashSet<>(); 1545 1546 for (String claimName: claimNames) { 1547 entries.add(new ClaimsRequest.Entry(claimName, ClaimRequirement.VOLUNTARY)); 1548 } 1549 1550 } else { 1551 1552 continue; // skip 1553 } 1554 1555 for (ClaimsRequest.Entry en : entries) { 1556 1557 if (switchToIDToken) 1558 claimsRequest.addIDTokenClaim(en); 1559 else 1560 claimsRequest.addUserInfoClaim(en); 1561 } 1562 } 1563 1564 return claimsRequest; 1565 } 1566 1567 1568 /** 1569 * Resolves the merged claims request from the specified OpenID 1570 * authentication request parameters. The scope values that are {@link 1571 * OIDCScopeValue standard OpenID scope values} are resolved to their 1572 * respective individual claims requests, any other scope values are 1573 * ignored. 1574 * 1575 * @param responseType The response type. Must not be {@code null}. 1576 * @param scope The scope, {@code null} if not specified (for a 1577 * plain OAuth 2.0 authorisation request with no 1578 * scope explicitly specified). 1579 * @param claimsRequest The claims request, corresponding to the 1580 * optional {@code claims} OpenID Connect 1581 * authorisation request parameter, {@code null} 1582 * if not specified. 1583 * 1584 * @return The merged claims request. 1585 */ 1586 public static ClaimsRequest resolve(final ResponseType responseType, 1587 final Scope scope, 1588 final ClaimsRequest claimsRequest) { 1589 1590 return resolve(responseType, scope, claimsRequest, Collections.<Scope.Value, Set<String>>emptyMap()); 1591 } 1592 1593 1594 /** 1595 * Resolves the merged claims request from the specified OpenID 1596 * authentication request parameters. The scope values that are {@link 1597 * OIDCScopeValue standard OpenID scope values} are resolved to their 1598 * respective individual claims requests, any other scope values are 1599 * checked in the specified custom claims map and resolved accordingly. 1600 * 1601 * @param responseType The response type. Must not be {@code null}. 1602 * @param scope The scope, {@code null} if not specified (for a 1603 * plain OAuth 2.0 authorisation request with no 1604 * scope explicitly specified). 1605 * @param claimsRequest The claims request, corresponding to the 1606 * optional {@code claims} OpenID Connect 1607 * authorisation request parameter, {@code null} 1608 * if not specified. 1609 * @param customClaims Custom scope value to set of claim names map, 1610 * {@code null} if not specified. 1611 * 1612 * @return The merged claims request. 1613 */ 1614 public static ClaimsRequest resolve(final ResponseType responseType, 1615 final Scope scope, 1616 final ClaimsRequest claimsRequest, 1617 final Map<Scope.Value, Set<String>> customClaims) { 1618 1619 ClaimsRequest mergedClaimsRequest = resolve(responseType, scope, customClaims); 1620 1621 mergedClaimsRequest.add(claimsRequest); 1622 1623 return mergedClaimsRequest; 1624 } 1625 1626 1627 /** 1628 * Resolves the merged claims request for the specified OpenID 1629 * authentication request. The scope values that are {@link 1630 * OIDCScopeValue standard OpenID scope values} are resolved to their 1631 * respective individual claims requests, any other scope values are 1632 * ignored. 1633 * 1634 * @param authRequest The OpenID authentication request. Must not be 1635 * {@code null}. 1636 * 1637 * @return The merged claims request. 1638 */ 1639 public static ClaimsRequest resolve(final AuthenticationRequest authRequest) { 1640 1641 return resolve(authRequest.getResponseType(), authRequest.getScope(), authRequest.getClaims()); 1642 } 1643 1644 1645 /** 1646 * Parses a claims request from the specified JSON object 1647 * representation. Unexpected members in the JSON object are silently 1648 * ignored. 1649 * 1650 * @param jsonObject The JSON object to parse. Must not be 1651 * {@code null}. 1652 * 1653 * @return The claims request. 1654 */ 1655 public static ClaimsRequest parse(final JSONObject jsonObject) { 1656 1657 ClaimsRequest claimsRequest = new ClaimsRequest(); 1658 1659 try { 1660 JSONObject idTokenObject = JSONObjectUtils.getJSONObject(jsonObject, "id_token", null); 1661 1662 if (idTokenObject != null) { 1663 1664 for (Entry entry : Entry.parseEntries(idTokenObject)) { 1665 if ("verified_claims".equals(entry.getClaimName())) { 1666 continue; //skip 1667 } 1668 claimsRequest.addIDTokenClaim(entry); 1669 } 1670 1671 JSONObject verifiedClaimsObject = JSONObjectUtils.getJSONObject(idTokenObject, "verified_claims", null); 1672 1673 if (verifiedClaimsObject != null) { 1674 // id_token -> verified_claims -> claims 1675 JSONObject claimsObject = JSONObjectUtils.getJSONObject(verifiedClaimsObject, "claims", null); 1676 if (claimsObject != null) { 1677 for (Entry entry : Entry.parseEntries(claimsObject)) { 1678 claimsRequest.addVerifiedIDTokenClaim(entry); 1679 } 1680 } 1681 // id_token -> verified_claims -> verification 1682 claimsRequest.setIDTokenClaimsVerificationJSONObject(JSONObjectUtils.getJSONObject(verifiedClaimsObject, "verification", null)); 1683 } 1684 } 1685 1686 JSONObject userInfoObject = JSONObjectUtils.getJSONObject(jsonObject, "userinfo", null); 1687 1688 if (userInfoObject != null) { 1689 1690 for (Entry entry : Entry.parseEntries(userInfoObject)) { 1691 if ("verified_claims".equals(entry.getClaimName())) { 1692 continue; //skip 1693 } 1694 claimsRequest.addUserInfoClaim(entry); 1695 } 1696 1697 JSONObject verifiedClaimsObject = JSONObjectUtils.getJSONObject(userInfoObject, "verified_claims", null); 1698 1699 if (verifiedClaimsObject != null) { 1700 // userinfo -> verified_claims -> claims 1701 JSONObject claimsObject = JSONObjectUtils.getJSONObject(verifiedClaimsObject, "claims", null); 1702 if (claimsObject != null) { 1703 for (Entry entry : Entry.parseEntries(claimsObject)) { 1704 claimsRequest.addVerifiedUserInfoClaim(entry); 1705 } 1706 } 1707 // userinfo -> verified_claims -> verification 1708 claimsRequest.setUserInfoClaimsVerificationJSONObject(JSONObjectUtils.getJSONObject(verifiedClaimsObject, "verification", null)); 1709 } 1710 } 1711 1712 } catch (Exception e) { 1713 1714 // Ignore 1715 } 1716 1717 return claimsRequest; 1718 } 1719 1720 1721 /** 1722 * Parses a claims request from the specified JSON object string 1723 * representation. Unexpected members in the JSON object are silently 1724 * ignored. 1725 * 1726 * @param json The JSON object string to parse. Must not be 1727 * {@code null}. 1728 * 1729 * @return The claims request. 1730 * 1731 * @throws ParseException If the string couldn't be parsed to a valid 1732 * JSON object. 1733 */ 1734 public static ClaimsRequest parse(final String json) 1735 throws ParseException { 1736 1737 return parse(JSONObjectUtils.parse(json)); 1738 } 1739}