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.oauth2.sdk.client; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.util.*; 024import javax.mail.internet.AddressException; 025import javax.mail.internet.InternetAddress; 026 027import com.nimbusds.jose.EncryptionMethod; 028import com.nimbusds.jose.JWEAlgorithm; 029import com.nimbusds.jose.JWSAlgorithm; 030import com.nimbusds.jose.jwk.JWKSet; 031import com.nimbusds.langtag.LangTag; 032import com.nimbusds.langtag.LangTagUtils; 033import com.nimbusds.oauth2.sdk.GrantType; 034import com.nimbusds.oauth2.sdk.ParseException; 035import com.nimbusds.oauth2.sdk.ResponseType; 036import com.nimbusds.oauth2.sdk.Scope; 037import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod; 038import com.nimbusds.oauth2.sdk.id.SoftwareID; 039import com.nimbusds.oauth2.sdk.id.SoftwareVersion; 040import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 041import net.minidev.json.JSONArray; 042import net.minidev.json.JSONObject; 043 044 045/** 046 * Client metadata. 047 * 048 * <p>Example client metadata, serialised to a JSON object: 049 * 050 * <pre> 051 * { 052 * "redirect_uris" : ["https://client.example.org/callback", 053 * "https://client.example.org/callback2"], 054 * "client_name" : "My Example Client", 055 * "client_name#ja-Jpan-JP" : "\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u540D", 056 * "token_endpoint_auth_method" : "client_secret_basic", 057 * "scope" : "read write dolphin", 058 * "logo_uri" : "https://client.example.org/logo.png", 059 * "jwks_uri" : "https://client.example.org/my_public_keys.jwks" 060 * } 061 * </pre> 062 * 063 * <p>Related specifications: 064 * 065 * <ul> 066 * <li>OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591), section 067 * 2. 068 * <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound 069 * Access Tokens (draft-ietf-oauth-mtls-12), sections 2.1.2 and 3.4. 070 * <li>Financial-grade API: JWT Secured Authorization Response Mode for 071 * OAuth 2.0 (JARM). 072 * </ul> 073 */ 074public class ClientMetadata { 075 076 077 /** 078 * The registered parameter names. 079 */ 080 private static final Set<String> REGISTERED_PARAMETER_NAMES; 081 082 083 static { 084 Set<String> p = new HashSet<>(); 085 086 p.add("redirect_uris"); 087 p.add("scope"); 088 p.add("response_types"); 089 p.add("grant_types"); 090 p.add("contacts"); 091 p.add("client_name"); 092 p.add("logo_uri"); 093 p.add("client_uri"); 094 p.add("policy_uri"); 095 p.add("tos_uri"); 096 p.add("token_endpoint_auth_method"); 097 p.add("token_endpoint_auth_signing_alg"); 098 p.add("jwks_uri"); 099 p.add("jwks"); 100 p.add("software_id"); 101 p.add("software_version"); 102 p.add("tls_client_certificate_bound_access_tokens"); 103 p.add("tls_client_auth_subject_dn"); 104 p.add("authorization_signed_response_alg"); 105 p.add("authorization_encrypted_response_alg"); 106 p.add("authorization_encrypted_response_enc"); 107 108 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 109 } 110 111 112 /** 113 * Redirect URIs. 114 */ 115 private Set<URI> redirectURIs; 116 117 118 /** 119 * The client OAuth 2.0 scope. 120 */ 121 private Scope scope; 122 123 124 /** 125 * The expected OAuth 2.0 response types. 126 */ 127 private Set<ResponseType> responseTypes; 128 129 130 /** 131 * The expected OAuth 2.0 grant types. 132 */ 133 private Set<GrantType> grantTypes; 134 135 136 /** 137 * Administrator email contacts for the client. 138 */ 139 private List<String> contacts; 140 141 142 /** 143 * The client name. 144 */ 145 private final Map<LangTag,String> nameEntries; 146 147 148 /** 149 * The client application logo. 150 */ 151 private final Map<LangTag,URI> logoURIEntries; 152 153 154 /** 155 * The client URI entries. 156 */ 157 private final Map<LangTag,URI> uriEntries; 158 159 160 /** 161 * The client policy for use of end-user data. 162 */ 163 private Map<LangTag,URI> policyURIEntries; 164 165 166 /** 167 * The client terms of service. 168 */ 169 private final Map<LangTag,URI> tosURIEntries; 170 171 172 /** 173 * Token endpoint authentication method. 174 */ 175 private ClientAuthenticationMethod authMethod; 176 177 178 /** 179 * The JSON Web Signature (JWS) algorithm required for 180 * {@code private_key_jwt} and {@code client_secret_jwt} 181 * authentication at the Token endpoint. 182 */ 183 private JWSAlgorithm authJWSAlg; 184 185 186 /** 187 * URI for this client's JSON Web Key (JWK) set containing key(s) that 188 * are used in signing requests to the server and key(s) for encrypting 189 * responses. 190 */ 191 private URI jwkSetURI; 192 193 194 /** 195 * Client's JSON Web Key (JWK) set containing key(s) that are used in 196 * signing requests to the server and key(s) for encrypting responses. 197 * Intended as an alternative to {@link #jwkSetURI} for native clients. 198 */ 199 private JWKSet jwkSet; 200 201 202 /** 203 * Identifier for the OAuth 2.0 client software. 204 */ 205 private SoftwareID softwareID; 206 207 208 /** 209 * Version identifier for the OAuth 2.0 client software. 210 */ 211 private SoftwareVersion softwareVersion; 212 213 214 /** 215 * Preference for TLS client certificate bound access tokens. 216 */ 217 private boolean tlsClientCertificateBoundAccessTokens = false; 218 219 220 /** 221 * The expected subject distinguished name (DN) of the client X.509 222 * certificate the in mutual TLS authentication. 223 */ 224 private String tlsClientAuthSubjectDN = null; 225 226 227 /** 228 * The JWS algorithm for JWT-encoded authorisation responses. 229 */ 230 private JWSAlgorithm authzJWSAlg; 231 232 233 /** 234 * The JWE algorithm for JWT-encoded authorisation responses. 235 */ 236 private JWEAlgorithm authzJWEAlg; 237 238 239 /** 240 * The encryption method for JWT-encoded authorisation responses. 241 */ 242 private EncryptionMethod authzJWEEnc; 243 244 245 /** 246 * The custom metadata fields. 247 */ 248 private JSONObject customFields; 249 250 251 /** 252 * Creates a new OAuth 2.0 client metadata instance. 253 */ 254 public ClientMetadata() { 255 256 nameEntries = new HashMap<>(); 257 logoURIEntries = new HashMap<>(); 258 uriEntries = new HashMap<>(); 259 policyURIEntries = new HashMap<>(); 260 policyURIEntries = new HashMap<>(); 261 tosURIEntries = new HashMap<>(); 262 customFields = new JSONObject(); 263 } 264 265 266 /** 267 * Creates a shallow copy of the specified OAuth 2.0 client metadata 268 * instance. 269 * 270 * @param metadata The client metadata to copy. Must not be 271 * {@code null}. 272 */ 273 public ClientMetadata(final ClientMetadata metadata) { 274 275 redirectURIs = metadata.redirectURIs; 276 scope = metadata.scope; 277 responseTypes = metadata.responseTypes; 278 grantTypes = metadata.grantTypes; 279 contacts = metadata.contacts; 280 nameEntries = metadata.nameEntries; 281 logoURIEntries = metadata.logoURIEntries; 282 uriEntries = metadata.uriEntries; 283 policyURIEntries = metadata.policyURIEntries; 284 tosURIEntries = metadata.tosURIEntries; 285 authMethod = metadata.authMethod; 286 authJWSAlg = metadata.authJWSAlg; 287 jwkSetURI = metadata.jwkSetURI; 288 jwkSet = metadata.getJWKSet(); 289 softwareID = metadata.softwareID; 290 softwareVersion = metadata.softwareVersion; 291 tlsClientCertificateBoundAccessTokens = metadata.tlsClientCertificateBoundAccessTokens; 292 tlsClientAuthSubjectDN = metadata.tlsClientAuthSubjectDN; 293 authzJWSAlg = metadata.authzJWSAlg; 294 authzJWEAlg = metadata.authzJWEAlg; 295 authzJWEEnc = metadata.authzJWEEnc; 296 customFields = metadata.customFields; 297 } 298 299 300 /** 301 * Gets the registered (standard) OAuth 2.0 client metadata parameter 302 * names. 303 * 304 * @return The registered parameter names, as an unmodifiable set. 305 */ 306 public static Set<String> getRegisteredParameterNames() { 307 308 return REGISTERED_PARAMETER_NAMES; 309 } 310 311 312 /** 313 * Gets the redirection URIs for this client. Corresponds to the 314 * {@code redirect_uris} client metadata field. 315 * 316 * @return The redirection URIs, {@code null} if not specified. 317 */ 318 public Set<URI> getRedirectionURIs() { 319 320 return redirectURIs; 321 } 322 323 324 /** 325 * Gets one of the redirection URIs for this client. Corresponds to the 326 * {@code redirect_uris} client metadata field. 327 * 328 * @return The redirection URI, {@code null} if not specified. 329 */ 330 public URI getRedirectionURI() { 331 332 if (redirectURIs != null && ! redirectURIs.isEmpty()) { 333 return redirectURIs.iterator().next(); 334 } else { 335 return null; 336 } 337 } 338 339 340 /** 341 * Gets the redirection URIs for this client as strings. Corresponds to 342 * the {@code redirect_uris} client metadata field. 343 * 344 * <p>This short-hand method is intended to enable string-based URI 345 * comparison. 346 * 347 * @return The redirection URIs as strings, {@code null} if not 348 * specified. 349 */ 350 public Set<String> getRedirectionURIStrings() { 351 352 if (redirectURIs == null) 353 return null; 354 355 Set<String> uriStrings = new HashSet<>(); 356 357 for (URI uri: redirectURIs) 358 uriStrings.add(uri.toString()); 359 360 return uriStrings; 361 } 362 363 364 /** 365 * Sets the redirection URIs for this client. Corresponds to the 366 * {@code redirect_uris} client metadata field. 367 * 368 * @param redirectURIs The redirection URIs, {@code null} if not 369 * specified. Valid redirection URIs must not 370 * contain a fragment. 371 */ 372 public void setRedirectionURIs(final Set<URI> redirectURIs) { 373 374 if (redirectURIs != null) { 375 // check URIs 376 for (URI uri: redirectURIs) { 377 if (uri == null) { 378 throw new IllegalArgumentException("The redirect_uri must not be null"); 379 } 380 if (uri.getFragment() != null) { 381 throw new IllegalArgumentException("The redirect_uri must not contain fragment"); 382 } 383 } 384 this.redirectURIs = redirectURIs; 385 } else { 386 this.redirectURIs = null; 387 } 388 } 389 390 391 /** 392 * Sets a single redirection URI for this client. Corresponds to the 393 * {@code redirect_uris} client metadata field. 394 * 395 * @param redirectURI The redirection URIs, {@code null} if not 396 * specified. A valid redirection URI must not 397 * contain a fragment. 398 */ 399 public void setRedirectionURI(final URI redirectURI) { 400 401 setRedirectionURIs(redirectURI != null ? Collections.singleton(redirectURI) : null); 402 } 403 404 405 /** 406 * Gets the scope values that the client can use when requesting access 407 * tokens. Corresponds to the {@code scope} client metadata field. 408 * 409 * @return The scope, {@code null} if not specified. 410 */ 411 public Scope getScope() { 412 413 return scope; 414 } 415 416 417 /** 418 * Checks if the scope matadata field is set and contains the specified 419 * scope value. 420 * 421 * @param scopeValue The scope value. Must not be {@code null}. 422 * 423 * @return {@code true} if the scope value is contained, else 424 * {@code false}. 425 */ 426 public boolean hasScopeValue(final Scope.Value scopeValue) { 427 428 return scope != null && scope.contains(scopeValue); 429 } 430 431 432 /** 433 * Sets the scope values that the client can use when requesting access 434 * tokens. Corresponds to the {@code scope} client metadata field. 435 * 436 * @param scope The scope, {@code null} if not specified. 437 */ 438 public void setScope(final Scope scope) { 439 440 this.scope = scope; 441 } 442 443 444 /** 445 * Gets the expected OAuth 2.0 response types. Corresponds to the 446 * {@code response_types} client metadata field. 447 * 448 * @return The response types, {@code null} if not specified. 449 */ 450 public Set<ResponseType> getResponseTypes() { 451 452 return responseTypes; 453 } 454 455 456 /** 457 * Sets the expected OAuth 2.0 response types. Corresponds to the 458 * {@code response_types} client metadata field. 459 * 460 * @param responseTypes The response types, {@code null} if not 461 * specified. 462 */ 463 public void setResponseTypes(final Set<ResponseType> responseTypes) { 464 465 this.responseTypes = responseTypes; 466 } 467 468 469 /** 470 * Gets the expected OAuth 2.0 grant types. Corresponds to the 471 * {@code grant_types} client metadata field. 472 * 473 * @return The grant types, {@code null} if not specified. 474 */ 475 public Set<GrantType> getGrantTypes() { 476 477 return grantTypes; 478 } 479 480 481 /** 482 * Sets the expected OAuth 2.0 grant types. Corresponds to the 483 * {@code grant_types} client metadata field. 484 * 485 * @param grantTypes The grant types, {@code null} if not specified. 486 */ 487 public void setGrantTypes(final Set<GrantType> grantTypes) { 488 489 this.grantTypes = grantTypes; 490 } 491 492 493 /** 494 * Gets the administrator email contacts for the client. Corresponds to 495 * the {@code contacts} client metadata field. 496 * 497 * <p>Use {@link #getEmailContacts()} instead. 498 * 499 * @return The administrator email contacts, {@code null} if not 500 * specified. 501 */ 502 @Deprecated 503 public List<InternetAddress> getContacts() { 504 505 if (contacts == null) 506 return null; 507 508 List<InternetAddress> addresses = new LinkedList<>(); 509 for (String s: contacts) { 510 if (s == null) continue; 511 try { 512 addresses.add(new InternetAddress(s, false)); 513 } catch (AddressException e) { 514 // ignore 515 } 516 } 517 return addresses; 518 } 519 520 521 /** 522 * Sets the administrator email contacts for the client. Corresponds to 523 * the {@code contacts} client metadata field. 524 * 525 * <p>Use {@link #setEmailContacts(List)} instead. 526 * 527 * @param contacts The administrator email contacts, {@code null} if 528 * not specified. 529 */ 530 @Deprecated 531 public void setContacts(final List<InternetAddress> contacts) { 532 533 if (contacts == null) { 534 this.contacts = null; 535 return; 536 } 537 538 List<String> addresses = new LinkedList<>(); 539 for (InternetAddress a: contacts) { 540 if (a != null) { 541 addresses.add(a.toString()); 542 } 543 } 544 this.contacts = addresses; 545 } 546 547 548 /** 549 * Gets the administrator email contacts for the client. Corresponds to 550 * the {@code contacts} client metadata field. 551 * 552 * @return The administrator email contacts, {@code null} if not 553 * specified. 554 */ 555 public List<String> getEmailContacts() { 556 557 return contacts; 558 } 559 560 561 /** 562 * Sets the administrator email contacts for the client. Corresponds to 563 * the {@code contacts} client metadata field. 564 * 565 * @param contacts The administrator email contacts, {@code null} if 566 * not specified. 567 */ 568 public void setEmailContacts(final List<String> contacts) { 569 570 this.contacts = contacts; 571 } 572 573 574 /** 575 * Gets the client name. Corresponds to the {@code client_name} client 576 * metadata field, with no language tag. 577 * 578 * @return The client name, {@code null} if not specified. 579 */ 580 public String getName() { 581 582 return getName(null); 583 } 584 585 586 /** 587 * Gets the client name. Corresponds to the {@code client_name} client 588 * metadata field, with an optional language tag. 589 * 590 * @param langTag The language tag of the entry, {@code null} to get 591 * the non-tagged entry. 592 * 593 * @return The client name, {@code null} if not specified. 594 */ 595 public String getName(final LangTag langTag) { 596 597 return nameEntries.get(langTag); 598 } 599 600 601 /** 602 * Gets the client name entries. Corresponds to the {@code client_name} 603 * client metadata field. 604 * 605 * @return The client name entries, empty map if none. 606 */ 607 public Map<LangTag,String> getNameEntries() { 608 609 return nameEntries; 610 } 611 612 613 /** 614 * Sets the client name. Corresponds to the {@code client_name} client 615 * metadata field, with no language tag. 616 * 617 * @param name The client name, {@code null} if not specified. 618 */ 619 public void setName(final String name) { 620 621 nameEntries.put(null, name); 622 } 623 624 625 /** 626 * Sets the client name. Corresponds to the {@code client_name} client 627 * metadata field, with an optional language tag. 628 * 629 * @param name The client name. Must not be {@code null}. 630 * @param langTag The language tag, {@code null} if not specified. 631 */ 632 public void setName(final String name, final LangTag langTag) { 633 634 nameEntries.put(langTag, name); 635 } 636 637 638 /** 639 * Gets the client application logo. Corresponds to the 640 * {@code logo_uri} client metadata field, with no language 641 * tag. 642 * 643 * @return The logo URI, {@code null} if not specified. 644 */ 645 public URI getLogoURI() { 646 647 return getLogoURI(null); 648 } 649 650 651 /** 652 * Gets the client application logo. Corresponds to the 653 * {@code logo_uri} client metadata field, with an optional 654 * language tag. 655 * 656 * @param langTag The language tag, {@code null} if not specified. 657 * 658 * @return The logo URI, {@code null} if not specified. 659 */ 660 public URI getLogoURI(final LangTag langTag) { 661 662 return logoURIEntries.get(langTag); 663 } 664 665 666 /** 667 * Gets the client application logo entries. Corresponds to the 668 * {@code logo_uri} client metadata field. 669 * 670 * @return The logo URI entries, empty map if none. 671 */ 672 public Map<LangTag,URI> getLogoURIEntries() { 673 674 return logoURIEntries; 675 } 676 677 678 /** 679 * Sets the client application logo. Corresponds to the 680 * {@code logo_uri} client metadata field, with no language 681 * tag. 682 * 683 * @param logoURI The logo URI, {@code null} if not specified. 684 */ 685 public void setLogoURI(final URI logoURI) { 686 687 logoURIEntries.put(null, logoURI); 688 } 689 690 691 /** 692 * Sets the client application logo. Corresponds to the 693 * {@code logo_uri} client metadata field, with an optional 694 * language tag. 695 * 696 * @param logoURI The logo URI. Must not be {@code null}. 697 * @param langTag The language tag, {@code null} if not specified. 698 */ 699 public void setLogoURI(final URI logoURI, final LangTag langTag) { 700 701 logoURIEntries.put(langTag, logoURI); 702 } 703 704 705 /** 706 * Gets the client home page. Corresponds to the {@code client_uri} 707 * client metadata field, with no language tag. 708 * 709 * @return The client URI, {@code null} if not specified. 710 */ 711 public URI getURI() { 712 713 return getURI(null); 714 } 715 716 717 /** 718 * Gets the client home page. Corresponds to the {@code client_uri} 719 * client metadata field, with an optional language tag. 720 * 721 * @param langTag The language tag, {@code null} if not specified. 722 * 723 * @return The client URI, {@code null} if not specified. 724 */ 725 public URI getURI(final LangTag langTag) { 726 727 return uriEntries.get(langTag); 728 } 729 730 731 /** 732 * Gets the client home page entries. Corresponds to the 733 * {@code client_uri} client metadata field. 734 * 735 * @return The client URI entries, empty map if none. 736 */ 737 public Map<LangTag,URI> getURIEntries() { 738 739 return uriEntries; 740 } 741 742 743 /** 744 * Sets the client home page. Corresponds to the {@code client_uri} 745 * client metadata field, with no language tag. 746 * 747 * @param uri The client URI, {@code null} if not specified. 748 */ 749 public void setURI(final URI uri) { 750 751 uriEntries.put(null, uri); 752 } 753 754 755 /** 756 * Sets the client home page. Corresponds to the {@code client_uri} 757 * client metadata field, with an optional language tag. 758 * 759 * @param uri The URI. Must not be {@code null}. 760 * @param langTag The language tag, {@code null} if not specified. 761 */ 762 public void setURI(final URI uri, final LangTag langTag) { 763 764 uriEntries.put(langTag, uri); 765 } 766 767 768 /** 769 * Gets the client policy for use of end-user data. Corresponds to the 770 * {@code policy_uri} client metadata field, with no language 771 * tag. 772 * 773 * @return The policy URI, {@code null} if not specified. 774 */ 775 public URI getPolicyURI() { 776 777 return getPolicyURI(null); 778 } 779 780 781 /** 782 * Gets the client policy for use of end-user data. Corresponds to the 783 * {@code policy_uri} client metadata field, with an optional 784 * language tag. 785 * 786 * @param langTag The language tag, {@code null} if not specified. 787 * 788 * @return The policy URI, {@code null} if not specified. 789 */ 790 public URI getPolicyURI(final LangTag langTag) { 791 792 return policyURIEntries.get(langTag); 793 } 794 795 796 /** 797 * Gets the client policy entries for use of end-user data. 798 * Corresponds to the {@code policy_uri} client metadata field. 799 * 800 * @return The policy URI entries, empty map if none. 801 */ 802 public Map<LangTag,URI> getPolicyURIEntries() { 803 804 return policyURIEntries; 805 } 806 807 808 /** 809 * Sets the client policy for use of end-user data. Corresponds to the 810 * {@code policy_uri} client metadata field, with no language 811 * tag. 812 * 813 * @param policyURI The policy URI, {@code null} if not specified. 814 */ 815 public void setPolicyURI(final URI policyURI) { 816 817 policyURIEntries.put(null, policyURI); 818 } 819 820 821 /** 822 * Sets the client policy for use of end-user data. Corresponds to the 823 * {@code policy_uri} client metadata field, with an optional 824 * language tag. 825 * 826 * @param policyURI The policy URI. Must not be {@code null}. 827 * @param langTag The language tag, {@code null} if not specified. 828 */ 829 public void setPolicyURI(final URI policyURI, final LangTag langTag) { 830 831 policyURIEntries.put(langTag, policyURI); 832 } 833 834 835 /** 836 * Gets the client's terms of service. Corresponds to the 837 * {@code tos_uri} client metadata field, with no language 838 * tag. 839 * 840 * @return The terms of service URI, {@code null} if not specified. 841 */ 842 public URI getTermsOfServiceURI() { 843 844 return getTermsOfServiceURI(null); 845 } 846 847 848 /** 849 * Gets the client's terms of service. Corresponds to the 850 * {@code tos_uri} client metadata field, with an optional 851 * language tag. 852 * 853 * @param langTag The language tag, {@code null} if not specified. 854 * 855 * @return The terms of service URI, {@code null} if not specified. 856 */ 857 public URI getTermsOfServiceURI(final LangTag langTag) { 858 859 return tosURIEntries.get(langTag); 860 } 861 862 863 /** 864 * Gets the client's terms of service entries. Corresponds to the 865 * {@code tos_uri} client metadata field. 866 * 867 * @return The terms of service URI entries, empty map if none. 868 */ 869 public Map<LangTag,URI> getTermsOfServiceURIEntries() { 870 871 return tosURIEntries; 872 } 873 874 875 /** 876 * Sets the client's terms of service. Corresponds to the 877 * {@code tos_uri} client metadata field, with no language 878 * tag. 879 * 880 * @param tosURI The terms of service URI, {@code null} if not 881 * specified. 882 */ 883 public void setTermsOfServiceURI(final URI tosURI) { 884 885 tosURIEntries.put(null, tosURI); 886 } 887 888 889 /** 890 * Sets the client's terms of service. Corresponds to the 891 * {@code tos_uri} client metadata field, with an optional 892 * language tag. 893 * 894 * @param tosURI The terms of service URI. Must not be {@code null}. 895 * @param langTag The language tag, {@code null} if not specified. 896 */ 897 public void setTermsOfServiceURI(final URI tosURI, final LangTag langTag) { 898 899 tosURIEntries.put(langTag, tosURI); 900 } 901 902 903 /** 904 * Gets the Token endpoint authentication method. Corresponds to the 905 * {@code token_endpoint_auth_method} client metadata field. 906 * 907 * @return The Token endpoint authentication method, {@code null} if 908 * not specified. 909 */ 910 public ClientAuthenticationMethod getTokenEndpointAuthMethod() { 911 912 return authMethod; 913 } 914 915 916 /** 917 * Sets the Token endpoint authentication method. Corresponds to the 918 * {@code token_endpoint_auth_method} client metadata field. 919 * 920 * @param authMethod The Token endpoint authentication method, 921 * {@code null} if not specified. 922 */ 923 public void setTokenEndpointAuthMethod(final ClientAuthenticationMethod authMethod) { 924 925 this.authMethod = authMethod; 926 } 927 928 929 /** 930 * Gets the JSON Web Signature (JWS) algorithm required for 931 * {@code private_key_jwt} and {@code client_secret_jwt} 932 * authentication at the Token endpoint. Corresponds to the 933 * {@code token_endpoint_auth_signing_alg} client metadata field. 934 * 935 * @return The JWS algorithm, {@code null} if not specified. 936 */ 937 public JWSAlgorithm getTokenEndpointAuthJWSAlg() { 938 939 return authJWSAlg; 940 } 941 942 943 /** 944 * Sets the JSON Web Signature (JWS) algorithm required for 945 * {@code private_key_jwt} and {@code client_secret_jwt} 946 * authentication at the Token endpoint. Corresponds to the 947 * {@code token_endpoint_auth_signing_alg} client metadata field. 948 * 949 * @param authJWSAlg The JWS algorithm, {@code null} if not specified. 950 */ 951 public void setTokenEndpointAuthJWSAlg(final JWSAlgorithm authJWSAlg) { 952 953 this.authJWSAlg = authJWSAlg; 954 } 955 956 957 /** 958 * Gets the URI for this client's JSON Web Key (JWK) set containing 959 * key(s) that are used in signing requests to the server and key(s) 960 * for encrypting responses. Corresponds to the {@code jwks_uri} client 961 * metadata field. 962 * 963 * @return The JWK set URI, {@code null} if not specified. 964 */ 965 public URI getJWKSetURI() { 966 967 return jwkSetURI; 968 } 969 970 971 /** 972 * Sets the URI for this client's JSON Web Key (JWK) set containing 973 * key(s) that are used in signing requests to the server and key(s) 974 * for encrypting responses. Corresponds to the {@code jwks_uri} client 975 * metadata field. 976 * 977 * @param jwkSetURI The JWK set URI, {@code null} if not specified. 978 */ 979 public void setJWKSetURI(final URI jwkSetURI) { 980 981 this.jwkSetURI = jwkSetURI; 982 } 983 984 985 /** 986 * Gets this client's JSON Web Key (JWK) set containing key(s) that are 987 * used in signing requests to the server and key(s) for encrypting 988 * responses. Intended as an alternative to {@link #getJWKSetURI} for 989 * native clients. Corresponds to the {@code jwks} client metadata 990 * field. 991 * 992 * @return The JWK set, {@code null} if not specified. 993 */ 994 public JWKSet getJWKSet() { 995 996 return jwkSet; 997 } 998 999 1000 /** 1001 * Sets this client's JSON Web Key (JWK) set containing key(s) that are 1002 * used in signing requests to the server and key(s) for encrypting 1003 * responses. Intended as an alternative to {@link #getJWKSetURI} for 1004 * native clients. Corresponds to the {@code jwks} client metadata 1005 * field. 1006 * 1007 * @param jwkSet The JWK set, {@code null} if not specified. 1008 */ 1009 public void setJWKSet(final JWKSet jwkSet) { 1010 1011 this.jwkSet = jwkSet; 1012 } 1013 1014 1015 /** 1016 * Gets the identifier for the OAuth 2.0 client software. Corresponds 1017 * to the {@code software_id} client metadata field. 1018 * 1019 * @return The software identifier, {@code null} if not specified. 1020 */ 1021 public SoftwareID getSoftwareID() { 1022 1023 return softwareID; 1024 } 1025 1026 1027 /** 1028 * Sets the identifier for the OAuth 2.0 client software. Corresponds 1029 * to the {@code software_id} client metadata field. 1030 * 1031 * @param softwareID The software identifier, {@code null} if not 1032 * specified. 1033 */ 1034 public void setSoftwareID(final SoftwareID softwareID) { 1035 1036 this.softwareID = softwareID; 1037 } 1038 1039 1040 /** 1041 * Gets the version identifier for the OAuth 2.0 client software. 1042 * Corresponds to the {@code software_version} client metadata field. 1043 * 1044 * @return The version identifier, {@code null} if not specified. 1045 */ 1046 public SoftwareVersion getSoftwareVersion() { 1047 1048 return softwareVersion; 1049 } 1050 1051 1052 /** 1053 * Sets the version identifier for the OAuth 2.0 client software. 1054 * Corresponds to the {@code software_version} client metadata field. 1055 * 1056 * @param softwareVersion The version identifier, {@code null} if not 1057 * specified. 1058 */ 1059 public void setSoftwareVersion(final SoftwareVersion softwareVersion) { 1060 1061 this.softwareVersion = softwareVersion; 1062 } 1063 1064 1065 /** 1066 * Sets the preference for TLS client certificate bound access tokens. 1067 * Corresponds to the 1068 * {@code tls_client_certificate_bound_access_tokens} client metadata 1069 * field. 1070 * 1071 * @return {@code true} indicates a preference for TLS client 1072 * certificate bound access tokens, {@code false} if none. 1073 */ 1074 public boolean getTLSClientCertificateBoundAccessTokens() { 1075 1076 return tlsClientCertificateBoundAccessTokens; 1077 } 1078 1079 1080 /** 1081 * Gets the preference for TLS client certificate bound access tokens. 1082 * Corresponds to the 1083 * {@code tls_client_certificate_bound_access_tokens} client metadata 1084 * field. 1085 * 1086 * @param tlsClientCertBoundTokens {@code true} indicates a preference 1087 * for TLS client certificate bound 1088 * access tokens, {@code false} if 1089 * none. 1090 */ 1091 public void setTLSClientCertificateBoundAccessTokens(final boolean tlsClientCertBoundTokens) { 1092 1093 tlsClientCertificateBoundAccessTokens = tlsClientCertBoundTokens; 1094 } 1095 1096 1097 /** 1098 * Sets the preference for TLS client certificate bound access tokens. 1099 * Corresponds to the 1100 * {@code tls_client_certificate_bound_access_tokens} client metadata 1101 * field. 1102 * 1103 * @return {@code true} indicates a preference for TLS client 1104 * certificate bound access tokens, {@code false} if none. 1105 */ 1106 @Deprecated 1107 public boolean getMutualTLSSenderConstrainedAccessTokens() { 1108 1109 return tlsClientCertificateBoundAccessTokens; 1110 } 1111 1112 1113 /** 1114 * Gets the preference for TLS client certificate bound access tokens. 1115 * Corresponds to the 1116 * {@code tls_client_certificate_bound_access_tokens} client metadata 1117 * field. 1118 * 1119 * @param tlsSenderAccessTokens {@code true} indicates a preference for 1120 * TLS client certificate bound access 1121 * tokens, {@code false} if none. 1122 */ 1123 @Deprecated 1124 public void setMutualTLSSenderConstrainedAccessTokens(final boolean tlsSenderAccessTokens) { 1125 1126 tlsClientCertificateBoundAccessTokens = tlsSenderAccessTokens; 1127 } 1128 1129 1130 /** 1131 * Gets the expected subject distinguished name (DN) of the client 1132 * X.509 certificate in mutual TLS authentication. Corresponds to the 1133 * {@code tls_client_auth_subject_dn} client metadata field. 1134 * 1135 * @return The expected subject distinguished name (DN) of the client 1136 * X.509 certificate, {@code null} if not specified. 1137 */ 1138 public String getTLSClientAuthSubjectDN() { 1139 1140 return tlsClientAuthSubjectDN; 1141 } 1142 1143 1144 /** 1145 * Sets the expected subject distinguished name (DN) of the client 1146 * X.509 certificate in mutual TLS authentication. Corresponds to the 1147 * {@code tls_client_auth_subject_dn} client metadata field. 1148 * 1149 * @param subjectDN The expected subject distinguished name (DN) of the 1150 * client X.509 certificate, {@code null} if not 1151 * specified. 1152 */ 1153 public void setTLSClientAuthSubjectDN(final String subjectDN) { 1154 1155 this.tlsClientAuthSubjectDN = subjectDN; 1156 } 1157 1158 1159 /** 1160 * Gets the JWS algorithm for JWT-encoded authorisation responses. 1161 * Corresponds to the {@code authorization_signed_response_alg} client 1162 * metadata field. 1163 * 1164 * @return The JWS algorithm, {@code null} if not specified. 1165 */ 1166 public JWSAlgorithm getAuthorizationJWSAlg() { 1167 1168 return authzJWSAlg; 1169 } 1170 1171 1172 /** 1173 * Sets the JWS algorithm for JWT-encoded authorisation responses. 1174 * Corresponds to the {@code authorization_signed_response_alg} client 1175 * metadata field. 1176 * 1177 * @param authzJWSAlg The JWS algorithm, {@code null} if not specified. 1178 * Must not be {@code "none"}. 1179 */ 1180 public void setAuthorizationJWSAlg(final JWSAlgorithm authzJWSAlg) { 1181 1182 if (new JWSAlgorithm("none").equals(authzJWSAlg)) { 1183 // Prevent passing none as JWS alg 1184 throw new IllegalArgumentException("The JWS algorithm must not be \"none\""); 1185 } 1186 1187 this.authzJWSAlg = authzJWSAlg; 1188 } 1189 1190 1191 /** 1192 * Gets the JWE algorithm for JWT-encoded authorisation responses. 1193 * Corresponds to the {@code authorization_encrypted_response_alg} 1194 * client metadata field. 1195 * 1196 * @return The JWE algorithm, {@code null} if not specified. 1197 */ 1198 public JWEAlgorithm getAuthorizationJWEAlg() { 1199 1200 return authzJWEAlg; 1201 } 1202 1203 1204 /** 1205 * Sets the JWE algorithm for JWT-encoded authorisation responses. 1206 * Corresponds to the {@code authorization_encrypted_response_alg} 1207 * client metadata field. 1208 * 1209 * @param authzJWEAlg The JWE algorithm, {@code null} if not specified. 1210 */ 1211 public void setAuthorizationJWEAlg(final JWEAlgorithm authzJWEAlg) { 1212 1213 this.authzJWEAlg = authzJWEAlg; 1214 } 1215 1216 1217 /** 1218 * Sets the encryption method for JWT-encoded authorisation responses. 1219 * Corresponds to the {@code authorization_encrypted_response_enc} 1220 * client metadata field. 1221 * 1222 * @return The encryption method, {@code null} if specified. 1223 */ 1224 public EncryptionMethod getAuthorizationJWEEnc() { 1225 1226 return authzJWEEnc; 1227 } 1228 1229 1230 /** 1231 * Sets the encryption method for JWT-encoded authorisation responses. 1232 * Corresponds to the {@code authorization_encrypted_response_enc} 1233 * client metadata field. 1234 * 1235 * @param authzJWEEnc The encryption method, {@code null} if specified. 1236 */ 1237 public void setAuthorizationJWEEnc(final EncryptionMethod authzJWEEnc) { 1238 1239 this.authzJWEEnc = authzJWEEnc; 1240 } 1241 1242 1243 /** 1244 * Gets the specified custom metadata field. 1245 * 1246 * @param name The field name. Must not be {@code null}. 1247 * 1248 * @return The field value, typically serialisable to a JSON entity, 1249 * {@code null} if none. 1250 */ 1251 public Object getCustomField(final String name) { 1252 1253 return customFields.get(name); 1254 } 1255 1256 1257 /** 1258 * Gets the custom metadata fields. 1259 * 1260 * @return The custom metadata fields, as a JSON object, empty object 1261 * if none. 1262 */ 1263 public JSONObject getCustomFields() { 1264 1265 return customFields; 1266 } 1267 1268 1269 /** 1270 * Sets the specified custom metadata field. 1271 * 1272 * @param name The field name. Must not be {@code null}. 1273 * @param value The field value. Should serialise to a JSON entity. 1274 */ 1275 public void setCustomField(final String name, final Object value) { 1276 1277 customFields.put(name, value); 1278 } 1279 1280 1281 /** 1282 * Sets the custom metadata fields. 1283 * 1284 * @param customFields The custom metadata fields, as a JSON object, 1285 * empty object if none. Must not be {@code null}. 1286 */ 1287 public void setCustomFields(final JSONObject customFields) { 1288 1289 if (customFields == null) 1290 throw new IllegalArgumentException("The custom fields JSON object must not be null"); 1291 1292 this.customFields = customFields; 1293 } 1294 1295 1296 /** 1297 * Applies the client metadata defaults where no values have been 1298 * specified. 1299 * 1300 * <ul> 1301 * <li>The response types default to {@code ["code"]}. 1302 * <li>The grant types default to {@code ["authorization_code"]}. 1303 * <li>The client authentication method defaults to 1304 * "client_secret_basic", unless the grant type is "implicit" 1305 * only. 1306 * <li>The encryption method for JWT-encoded authorisation 1307 * responses defaults to {@code A128CBC-HS256} if a JWE 1308 * algorithm is set. 1309 * </ul> 1310 */ 1311 public void applyDefaults() { 1312 1313 if (responseTypes == null) { 1314 responseTypes = new HashSet<>(); 1315 responseTypes.add(ResponseType.getDefault()); 1316 } 1317 1318 if (grantTypes == null) { 1319 grantTypes = new HashSet<>(); 1320 grantTypes.add(GrantType.AUTHORIZATION_CODE); 1321 } 1322 1323 if (authMethod == null) { 1324 1325 if (grantTypes.contains(GrantType.IMPLICIT) && grantTypes.size() == 1) { 1326 authMethod = ClientAuthenticationMethod.NONE; 1327 } else { 1328 authMethod = ClientAuthenticationMethod.getDefault(); 1329 } 1330 } 1331 1332 if (authzJWEAlg != null && authzJWEEnc == null) { 1333 authzJWEEnc = EncryptionMethod.A128CBC_HS256; 1334 } 1335 } 1336 1337 1338 /** 1339 * Returns the JSON object representation of this client metadata, 1340 * including any custom fields. 1341 * 1342 * @return The JSON object. 1343 */ 1344 public JSONObject toJSONObject() { 1345 1346 return toJSONObject(true); 1347 } 1348 1349 1350 /** 1351 * Returns the JSON object representation of this client metadata. 1352 * 1353 * @param includeCustomFields {@code true} to include any custom 1354 * metadata fields, {@code false} to omit 1355 * them. 1356 * 1357 * @return The JSON object. 1358 */ 1359 public JSONObject toJSONObject(final boolean includeCustomFields) { 1360 1361 JSONObject o; 1362 1363 if (includeCustomFields) 1364 o = new JSONObject(customFields); 1365 else 1366 o = new JSONObject(); 1367 1368 1369 if (redirectURIs != null) { 1370 1371 JSONArray uriList = new JSONArray(); 1372 1373 for (URI uri: redirectURIs) 1374 uriList.add(uri.toString()); 1375 1376 o.put("redirect_uris", uriList); 1377 } 1378 1379 1380 if (scope != null) 1381 o.put("scope", scope.toString()); 1382 1383 1384 if (responseTypes != null) { 1385 1386 JSONArray rtList = new JSONArray(); 1387 1388 for (ResponseType rt: responseTypes) 1389 rtList.add(rt.toString()); 1390 1391 o.put("response_types", rtList); 1392 } 1393 1394 1395 if (grantTypes != null) { 1396 1397 JSONArray grantList = new JSONArray(); 1398 1399 for (GrantType grant: grantTypes) 1400 grantList.add(grant.toString()); 1401 1402 o.put("grant_types", grantList); 1403 } 1404 1405 1406 if (contacts != null) { 1407 o.put("contacts", contacts); 1408 } 1409 1410 1411 if (! nameEntries.isEmpty()) { 1412 1413 for (Map.Entry<LangTag,String> entry: nameEntries.entrySet()) { 1414 1415 LangTag langTag = entry.getKey(); 1416 String name = entry.getValue(); 1417 1418 if (name == null) 1419 continue; 1420 1421 if (langTag == null) 1422 o.put("client_name", entry.getValue()); 1423 else 1424 o.put("client_name#" + langTag, entry.getValue()); 1425 } 1426 } 1427 1428 1429 if (! logoURIEntries.isEmpty()) { 1430 1431 for (Map.Entry<LangTag,URI> entry: logoURIEntries.entrySet()) { 1432 1433 LangTag langTag = entry.getKey(); 1434 URI uri = entry.getValue(); 1435 1436 if (uri == null) 1437 continue; 1438 1439 if (langTag == null) 1440 o.put("logo_uri", entry.getValue().toString()); 1441 else 1442 o.put("logo_uri#" + langTag, entry.getValue().toString()); 1443 } 1444 } 1445 1446 1447 if (! uriEntries.isEmpty()) { 1448 1449 for (Map.Entry<LangTag,URI> entry: uriEntries.entrySet()) { 1450 1451 LangTag langTag = entry.getKey(); 1452 URI uri = entry.getValue(); 1453 1454 if (uri == null) 1455 continue; 1456 1457 if (langTag == null) 1458 o.put("client_uri", entry.getValue().toString()); 1459 else 1460 o.put("client_uri#" + langTag, entry.getValue().toString()); 1461 } 1462 } 1463 1464 1465 if (! policyURIEntries.isEmpty()) { 1466 1467 for (Map.Entry<LangTag,URI> entry: policyURIEntries.entrySet()) { 1468 1469 LangTag langTag = entry.getKey(); 1470 URI uri = entry.getValue(); 1471 1472 if (uri == null) 1473 continue; 1474 1475 if (langTag == null) 1476 o.put("policy_uri", entry.getValue().toString()); 1477 else 1478 o.put("policy_uri#" + langTag, entry.getValue().toString()); 1479 } 1480 } 1481 1482 1483 if (! tosURIEntries.isEmpty()) { 1484 1485 for (Map.Entry<LangTag,URI> entry: tosURIEntries.entrySet()) { 1486 1487 LangTag langTag = entry.getKey(); 1488 URI uri = entry.getValue(); 1489 1490 if (uri == null) 1491 continue; 1492 1493 if (langTag == null) 1494 o.put("tos_uri", entry.getValue().toString()); 1495 else 1496 o.put("tos_uri#" + langTag, entry.getValue().toString()); 1497 } 1498 } 1499 1500 1501 if (authMethod != null) 1502 o.put("token_endpoint_auth_method", authMethod.toString()); 1503 1504 1505 if (authJWSAlg != null) 1506 o.put("token_endpoint_auth_signing_alg", authJWSAlg.getName()); 1507 1508 1509 if (jwkSetURI != null) 1510 o.put("jwks_uri", jwkSetURI.toString()); 1511 1512 1513 if (jwkSet != null) 1514 o.put("jwks", jwkSet.toJSONObject(true)); // prevent private keys from leaking 1515 1516 1517 if (softwareID != null) 1518 o.put("software_id", softwareID.getValue()); 1519 1520 if (softwareVersion != null) 1521 o.put("software_version", softwareVersion.getValue()); 1522 1523 o.put("tls_client_certificate_bound_access_tokens", tlsClientCertificateBoundAccessTokens); 1524 1525 if (tlsClientAuthSubjectDN != null) 1526 o.put("tls_client_auth_subject_dn", tlsClientAuthSubjectDN); 1527 1528 if (authzJWSAlg != null) { 1529 o.put("authorization_signed_response_alg", authzJWSAlg.getName()); 1530 } 1531 1532 if (authzJWEAlg != null) { 1533 o.put("authorization_encrypted_response_alg", authzJWEAlg.getName()); 1534 } 1535 1536 if (authzJWEEnc != null) { 1537 o.put("authorization_encrypted_response_enc", authzJWEEnc.getName()); 1538 } 1539 1540 return o; 1541 } 1542 1543 1544 @Override 1545 public String toString() { 1546 return toJSONObject().toJSONString(); 1547 } 1548 1549 1550 /** 1551 * Parses an client metadata instance from the specified JSON object. 1552 * 1553 * @param jsonObject The JSON object to parse. Must not be 1554 * {@code null}. 1555 * 1556 * @return The client metadata. 1557 * 1558 * @throws ParseException If the JSON object couldn't be parsed to a 1559 * client metadata instance. 1560 */ 1561 public static ClientMetadata parse(final JSONObject jsonObject) 1562 throws ParseException { 1563 1564 // Copy JSON object, then parse 1565 return parseFromModifiableJSONObject(new JSONObject(jsonObject)); 1566 } 1567 1568 1569 /** 1570 * Parses an client metadata instance from the specified JSON object. 1571 * 1572 * @param jsonObject The JSON object to parse, will be modified by 1573 * the parse routine. Must not be {@code null}. 1574 * 1575 * @return The client metadata. 1576 * 1577 * @throws ParseException If the JSON object couldn't be parsed to a 1578 * client metadata instance. 1579 */ 1580 private static ClientMetadata parseFromModifiableJSONObject(final JSONObject jsonObject) 1581 throws ParseException { 1582 1583 ClientMetadata metadata = new ClientMetadata(); 1584 1585 if (jsonObject.get("redirect_uris") != null) { 1586 1587 Set<URI> redirectURIs = new LinkedHashSet<>(); 1588 1589 for (String uriString: JSONObjectUtils.getStringArray(jsonObject, "redirect_uris")) { 1590 URI uri; 1591 try { 1592 uri = new URI(uriString); 1593 } catch (URISyntaxException e) { 1594 throw new ParseException("Invalid \"redirect_uris\" parameter: " + e.getMessage(), RegistrationError.INVALID_REDIRECT_URI.appendDescription(": " + e.getMessage())); 1595 } 1596 1597 if (uri.getFragment() != null) { 1598 String detail = "URI must not contain fragment"; 1599 throw new ParseException("Invalid \"redirect_uris\" parameter: " + detail, RegistrationError.INVALID_REDIRECT_URI.appendDescription(": " + detail)); 1600 } 1601 1602 redirectURIs.add(uri); 1603 } 1604 1605 metadata.setRedirectionURIs(redirectURIs); 1606 jsonObject.remove("redirect_uris"); 1607 } 1608 1609 try { 1610 1611 if (jsonObject.get("scope") != null) { 1612 metadata.setScope(Scope.parse(JSONObjectUtils.getString(jsonObject, "scope"))); 1613 jsonObject.remove("scope"); 1614 } 1615 1616 1617 if (jsonObject.get("response_types") != null) { 1618 1619 Set<ResponseType> responseTypes = new LinkedHashSet<>(); 1620 1621 for (String rt : JSONObjectUtils.getStringArray(jsonObject, "response_types")) { 1622 1623 responseTypes.add(ResponseType.parse(rt)); 1624 } 1625 1626 metadata.setResponseTypes(responseTypes); 1627 jsonObject.remove("response_types"); 1628 } 1629 1630 1631 if (jsonObject.get("grant_types") != null) { 1632 1633 Set<GrantType> grantTypes = new LinkedHashSet<>(); 1634 1635 for (String grant : JSONObjectUtils.getStringArray(jsonObject, "grant_types")) { 1636 1637 grantTypes.add(GrantType.parse(grant)); 1638 } 1639 1640 metadata.setGrantTypes(grantTypes); 1641 jsonObject.remove("grant_types"); 1642 } 1643 1644 1645 if (jsonObject.get("contacts") != null) { 1646 metadata.setEmailContacts(JSONObjectUtils.getStringList(jsonObject, "contacts")); 1647 jsonObject.remove("contacts"); 1648 } 1649 1650 1651 // Find lang-tagged client_name params 1652 Map<LangTag, Object> matches = LangTagUtils.find("client_name", jsonObject); 1653 1654 for (Map.Entry<LangTag, Object> entry : matches.entrySet()) { 1655 1656 try { 1657 metadata.setName((String) entry.getValue(), entry.getKey()); 1658 1659 } catch (ClassCastException e) { 1660 1661 throw new ParseException("Invalid \"client_name\" (language tag) parameter"); 1662 } 1663 1664 removeMember(jsonObject, "client_name", entry.getKey()); 1665 } 1666 1667 1668 matches = LangTagUtils.find("logo_uri", jsonObject); 1669 1670 for (Map.Entry<LangTag, Object> entry : matches.entrySet()) { 1671 1672 if (entry.getValue() == null) continue; 1673 1674 try { 1675 metadata.setLogoURI(new URI((String) entry.getValue()), entry.getKey()); 1676 1677 } catch (Exception e) { 1678 1679 throw new ParseException("Invalid \"logo_uri\" (language tag) parameter"); 1680 } 1681 1682 removeMember(jsonObject, "logo_uri", entry.getKey()); 1683 } 1684 1685 1686 matches = LangTagUtils.find("client_uri", jsonObject); 1687 1688 for (Map.Entry<LangTag, Object> entry : matches.entrySet()) { 1689 1690 if (entry.getValue() == null) continue; 1691 1692 try { 1693 metadata.setURI(new URI((String) entry.getValue()), entry.getKey()); 1694 1695 1696 } catch (Exception e) { 1697 1698 throw new ParseException("Invalid \"client_uri\" (language tag) parameter"); 1699 } 1700 1701 removeMember(jsonObject, "client_uri", entry.getKey()); 1702 } 1703 1704 1705 matches = LangTagUtils.find("policy_uri", jsonObject); 1706 1707 for (Map.Entry<LangTag, Object> entry : matches.entrySet()) { 1708 1709 if (entry.getValue() == null) continue; 1710 1711 try { 1712 metadata.setPolicyURI(new URI((String) entry.getValue()), entry.getKey()); 1713 1714 } catch (Exception e) { 1715 1716 throw new ParseException("Invalid \"policy_uri\" (language tag) parameter"); 1717 } 1718 1719 removeMember(jsonObject, "policy_uri", entry.getKey()); 1720 } 1721 1722 1723 matches = LangTagUtils.find("tos_uri", jsonObject); 1724 1725 for (Map.Entry<LangTag, Object> entry : matches.entrySet()) { 1726 1727 if (entry.getValue() == null) continue; 1728 1729 try { 1730 metadata.setTermsOfServiceURI(new URI((String) entry.getValue()), entry.getKey()); 1731 1732 } catch (Exception e) { 1733 1734 throw new ParseException("Invalid \"tos_uri\" (language tag) parameter"); 1735 } 1736 1737 removeMember(jsonObject, "tos_uri", entry.getKey()); 1738 } 1739 1740 1741 if (jsonObject.get("token_endpoint_auth_method") != null) { 1742 metadata.setTokenEndpointAuthMethod(ClientAuthenticationMethod.parse( 1743 JSONObjectUtils.getString(jsonObject, "token_endpoint_auth_method"))); 1744 1745 jsonObject.remove("token_endpoint_auth_method"); 1746 } 1747 1748 1749 if (jsonObject.get("token_endpoint_auth_signing_alg") != null) { 1750 metadata.setTokenEndpointAuthJWSAlg(JWSAlgorithm.parse( 1751 JSONObjectUtils.getString(jsonObject, "token_endpoint_auth_signing_alg"))); 1752 1753 jsonObject.remove("token_endpoint_auth_signing_alg"); 1754 } 1755 1756 1757 if (jsonObject.get("jwks_uri") != null) { 1758 metadata.setJWKSetURI(JSONObjectUtils.getURI(jsonObject, "jwks_uri")); 1759 jsonObject.remove("jwks_uri"); 1760 } 1761 1762 if (jsonObject.get("jwks") != null) { 1763 1764 try { 1765 metadata.setJWKSet(JWKSet.parse(JSONObjectUtils.getJSONObject(jsonObject, "jwks"))); 1766 1767 } catch (java.text.ParseException e) { 1768 throw new ParseException(e.getMessage(), e); 1769 } 1770 1771 jsonObject.remove("jwks"); 1772 } 1773 1774 if (jsonObject.get("software_id") != null) { 1775 metadata.setSoftwareID(new SoftwareID(JSONObjectUtils.getString(jsonObject, "software_id"))); 1776 jsonObject.remove("software_id"); 1777 } 1778 1779 if (jsonObject.get("software_version") != null) { 1780 metadata.setSoftwareVersion(new SoftwareVersion(JSONObjectUtils.getString(jsonObject, "software_version"))); 1781 jsonObject.remove("software_version"); 1782 } 1783 1784 if (jsonObject.get("tls_client_certificate_bound_access_tokens") != null) { 1785 metadata.setTLSClientCertificateBoundAccessTokens(JSONObjectUtils.getBoolean(jsonObject, "tls_client_certificate_bound_access_tokens")); 1786 jsonObject.remove("tls_client_certificate_bound_access_tokens"); 1787 } 1788 1789 if (jsonObject.get("tls_client_auth_subject_dn") != null) { 1790 metadata.setTLSClientAuthSubjectDN(JSONObjectUtils.getString(jsonObject, "tls_client_auth_subject_dn")); 1791 jsonObject.remove("tls_client_auth_subject_dn"); 1792 } 1793 1794 if (jsonObject.get("authorization_signed_response_alg") != null) { 1795 metadata.setAuthorizationJWSAlg(JWSAlgorithm.parse(JSONObjectUtils.getString(jsonObject, "authorization_signed_response_alg"))); 1796 jsonObject.remove("authorization_signed_response_alg"); 1797 } 1798 1799 if (jsonObject.get("authorization_encrypted_response_alg") != null) { 1800 metadata.setAuthorizationJWEAlg(JWEAlgorithm.parse(JSONObjectUtils.getString(jsonObject, "authorization_encrypted_response_alg"))); 1801 jsonObject.remove("authorization_encrypted_response_alg"); 1802 } 1803 1804 if (jsonObject.get("authorization_encrypted_response_enc") != null) { 1805 metadata.setAuthorizationJWEEnc(EncryptionMethod.parse(JSONObjectUtils.getString(jsonObject, "authorization_encrypted_response_enc"))); 1806 jsonObject.remove("authorization_encrypted_response_enc"); 1807 } 1808 1809 } catch (ParseException e) { 1810 // Insert client_client_metadata error code so that it 1811 // can be reported back to the client if we have a 1812 // registration event 1813 throw new ParseException(e.getMessage(), RegistrationError.INVALID_CLIENT_METADATA.appendDescription(": " + e.getMessage()), e.getCause()); 1814 } 1815 1816 // The remaining fields are custom 1817 metadata.customFields = jsonObject; 1818 1819 return metadata; 1820 } 1821 1822 1823 /** 1824 * Removes a JSON object member with the specified base name and 1825 * optional language tag. 1826 * 1827 * @param jsonObject The JSON object. Must not be {@code null}. 1828 * @param name The base member name. Must not be {@code null}. 1829 * @param langTag The language tag, {@code null} if none. 1830 */ 1831 private static void removeMember(final JSONObject jsonObject, final String name, final LangTag langTag) { 1832 1833 if (langTag == null) 1834 jsonObject.remove(name); 1835 else 1836 jsonObject.remove(name + "#" + langTag); 1837 } 1838}