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.rp; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.util.*; 024 025import net.minidev.json.JSONArray; 026import net.minidev.json.JSONObject; 027 028import com.nimbusds.jose.EncryptionMethod; 029import com.nimbusds.jose.JWEAlgorithm; 030import com.nimbusds.jose.JWSAlgorithm; 031import com.nimbusds.oauth2.sdk.ParseException; 032import com.nimbusds.oauth2.sdk.client.ClientMetadata; 033import com.nimbusds.oauth2.sdk.client.RegistrationError; 034import com.nimbusds.oauth2.sdk.util.CollectionUtils; 035import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 036import com.nimbusds.oauth2.sdk.util.URIUtils; 037import com.nimbusds.openid.connect.sdk.SubjectType; 038import com.nimbusds.openid.connect.sdk.assurance.evidences.attachment.HashAlgorithm; 039import com.nimbusds.openid.connect.sdk.claims.ACR; 040import com.nimbusds.openid.connect.sdk.id.SectorID; 041 042 043/** 044 * OpenID Connect client metadata. 045 * 046 * <p>Related specifications: 047 * 048 * <ul> 049 * <li>OpenID Connect Dynamic Client Registration 1.0, section 2. 050 * <li>OpenID Connect Session Management 1.0, section 5.1.1 (draft 28). 051 * <li>OpenID Connect Front-Channel Logout 1.0, section 2 (draft 02). 052 * <li>OpenID Connect Back-Channel Logout 1.0, section 2.2 (draft 04). 053 * <li>OpenID Connect for Identity Assurance 1.0 (draft 12). 054 * <li>OpenID Connect Federation 1.0 (draft 14). 055 * <li>OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591), section 056 * 2. 057 * <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound 058 * Access Tokens (RFC 8705), sections 2.1.2 and 3.4. 059 * <li>Financial-grade API: JWT Secured Authorization Response Mode for 060 * OAuth 2.0 (JARM) 061 * </ul> 062 */ 063public class OIDCClientMetadata extends ClientMetadata { 064 065 066 /** 067 * The registered parameter names. 068 */ 069 private static final Set<String> REGISTERED_PARAMETER_NAMES; 070 071 072 static { 073 // Start with the base OAuth 2.0 client params 074 Set<String> p = new HashSet<>(ClientMetadata.getRegisteredParameterNames()); 075 076 // OIDC params 077 p.add("application_type"); 078 p.add("subject_type"); 079 p.add("sector_identifier_uri"); 080 p.add("id_token_signed_response_alg"); 081 p.add("id_token_encrypted_response_alg"); 082 p.add("id_token_encrypted_response_enc"); 083 p.add("userinfo_signed_response_alg"); 084 p.add("userinfo_encrypted_response_alg"); 085 p.add("userinfo_encrypted_response_enc"); 086 p.add("default_max_age"); 087 p.add("require_auth_time"); 088 p.add("default_acr_values"); 089 p.add("initiate_login_uri"); 090 091 // OIDC session 092 p.add("post_logout_redirect_uris"); 093 094 // OIDC logout 095 p.add("frontchannel_logout_uri"); 096 p.add("frontchannel_logout_session_required"); 097 p.add("backchannel_logout_uri"); 098 p.add("backchannel_logout_session_required"); 099 100 // OIDC identity assurance 101 p.add("digest_algorithm"); 102 103 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 104 } 105 106 107 /** 108 * The client application type. 109 */ 110 private ApplicationType applicationType; 111 112 113 /** 114 * The subject identifier type for responses to this client. 115 */ 116 private SubjectType subjectType; 117 118 119 /** 120 * Sector identifier URI. 121 */ 122 private URI sectorIDURI; 123 124 125 /** 126 * The JSON Web Signature (JWS) algorithm required for the ID Tokens 127 * issued to this client. 128 */ 129 private JWSAlgorithm idTokenJWSAlg; 130 131 132 /** 133 * The JSON Web Encryption (JWE) algorithm required for the ID Tokens 134 * issued to this client. 135 */ 136 private JWEAlgorithm idTokenJWEAlg; 137 138 139 /** 140 * The JSON Web Encryption (JWE) method required for the ID Tokens 141 * issued to this client. 142 */ 143 private EncryptionMethod idTokenJWEEnc; 144 145 146 /** 147 * The JSON Web Signature (JWS) algorithm required for the UserInfo 148 * responses to this client. 149 */ 150 private JWSAlgorithm userInfoJWSAlg; 151 152 153 /** 154 * The JSON Web Encryption (JWE) algorithm required for the UserInfo 155 * responses to this client. 156 */ 157 private JWEAlgorithm userInfoJWEAlg; 158 159 160 /** 161 * The JSON Web Encryption (JWE) method required for the UserInfo 162 * responses to this client. 163 */ 164 private EncryptionMethod userInfoJWEEnc; 165 166 167 /** 168 * The default max authentication age, in seconds. If not specified 0. 169 */ 170 private int defaultMaxAge = -1; 171 172 173 /** 174 * If {@code true} the {@code auth_time} claim in the ID Token is 175 * required by default. 176 */ 177 private boolean requiresAuthTime; 178 179 180 /** 181 * The default Authentication Context Class Reference (ACR) values, by 182 * order of preference. 183 */ 184 private List<ACR> defaultACRs; 185 186 187 /** 188 * Authorisation server initiated login HTTPS URI. 189 */ 190 private URI initiateLoginURI; 191 192 193 /** 194 * Logout redirection URIs. 195 */ 196 private Set<URI> postLogoutRedirectURIs; 197 198 199 /** 200 * Front-channel logout URI. 201 */ 202 private URI frontChannelLogoutURI; 203 204 205 /** 206 * Indicates requirement for a session identifier on front-channel 207 * logout. 208 */ 209 private boolean frontChannelLogoutSessionRequired = false; 210 211 212 /** 213 * Back-channel logout URI. 214 */ 215 private URI backChannelLogoutURI; 216 217 218 /** 219 * Indicates requirement for a session identifier on back-channel 220 * logout. 221 */ 222 private boolean backChannelLogoutSessionRequired = false; 223 224 225 /** 226 * The digest algorithms for external attachments in OpenID Connect 227 * for Identity Assurance 1.0. 228 */ 229 private HashAlgorithm attachmentDigestAlg; 230 231 232 /** 233 * Creates a new OpenID Connect client metadata instance. 234 */ 235 public OIDCClientMetadata() { 236 237 super(); 238 } 239 240 241 /** 242 * Creates a new OpenID Connect client metadata instance from the 243 * specified base OAuth 2.0 client metadata. 244 * 245 * @param metadata The base OAuth 2.0 client metadata. Must not be 246 * {@code null}. 247 */ 248 public OIDCClientMetadata(final ClientMetadata metadata) { 249 250 super(metadata); 251 } 252 253 254 /** 255 * Creates a shallow copy of the specified OpenID Connect client 256 * metadata instance. 257 * 258 * @param metadata The client metadata to copy. Must not be 259 * {@code null}. 260 */ 261 public OIDCClientMetadata(final OIDCClientMetadata metadata) { 262 263 super(metadata); 264 applicationType = metadata.getApplicationType(); 265 subjectType = metadata.getSubjectType(); 266 sectorIDURI = metadata.getSectorIDURI(); 267 idTokenJWSAlg = metadata.getIDTokenJWSAlg(); 268 idTokenJWEAlg = metadata.getIDTokenJWEAlg(); 269 idTokenJWEEnc = metadata.getIDTokenJWEEnc(); 270 userInfoJWSAlg = metadata.getUserInfoJWSAlg(); 271 userInfoJWEAlg = metadata.getUserInfoJWEAlg(); 272 userInfoJWEEnc = metadata.getUserInfoJWEEnc(); 273 defaultMaxAge = metadata.getDefaultMaxAge(); 274 requiresAuthTime = metadata.requiresAuthTime(); 275 defaultACRs = metadata.getDefaultACRs(); 276 initiateLoginURI = metadata.getInitiateLoginURI(); 277 postLogoutRedirectURIs = metadata.getPostLogoutRedirectionURIs(); 278 frontChannelLogoutURI = metadata.getFrontChannelLogoutURI(); 279 frontChannelLogoutSessionRequired = metadata.requiresFrontChannelLogoutSession(); 280 backChannelLogoutURI = metadata.getBackChannelLogoutURI(); 281 backChannelLogoutSessionRequired = metadata.requiresBackChannelLogoutSession(); 282 attachmentDigestAlg = metadata.getAttachmentDigestAlg(); 283 } 284 285 286 /** 287 * Gets the registered (standard) OpenID Connect client metadata 288 * parameter names. 289 * 290 * @return The registered OpenID Connect parameter names, as an 291 * unmodifiable set. 292 */ 293 public static Set<String> getRegisteredParameterNames() { 294 295 return REGISTERED_PARAMETER_NAMES; 296 } 297 298 299 /** 300 * Gets the client application type. Corresponds to the 301 * {@code application_type} client metadata field. 302 * 303 * @return The client application type, {@code null} if not specified. 304 */ 305 public ApplicationType getApplicationType() { 306 307 return applicationType; 308 } 309 310 311 /** 312 * Sets the client application type. Corresponds to the 313 * {@code application_type} client metadata field. 314 * 315 * @param applicationType The client application type, {@code null} if 316 * not specified. 317 */ 318 public void setApplicationType(final ApplicationType applicationType) { 319 320 this.applicationType = applicationType; 321 } 322 323 324 /** 325 * Gets the subject identifier type for responses to this client. 326 * Corresponds to the {@code subject_type} client metadata field. 327 * 328 * @return The subject identifier type, {@code null} if not specified. 329 */ 330 public SubjectType getSubjectType() { 331 332 return subjectType; 333 } 334 335 336 /** 337 * Sets the subject identifier type for responses to this client. 338 * Corresponds to the {@code subject_type} client metadata field. 339 * 340 * @param subjectType The subject identifier type, {@code null} if not 341 * specified. 342 */ 343 public void setSubjectType(final SubjectType subjectType) { 344 345 this.subjectType = subjectType; 346 } 347 348 349 /** 350 * Gets the sector identifier URI. Corresponds to the 351 * {@code sector_identifier_uri} client metadata field. 352 * 353 * @return The sector identifier URI, {@code null} if not specified. 354 */ 355 public URI getSectorIDURI() { 356 357 return sectorIDURI; 358 } 359 360 361 /** 362 * Sets the sector identifier URI. Corresponds to the 363 * {@code sector_identifier_uri} client metadata field. If set the URI 364 * will be checked for having an {@code https} scheme and a host 365 * component unless the URI is an URN. 366 * 367 * @param sectorIDURI The sector identifier URI, {@code null} if not 368 * specified. 369 * 370 * @throws IllegalArgumentException If the URI was found to be illegal. 371 */ 372 public void setSectorIDURI(final URI sectorIDURI) { 373 374 if (sectorIDURI != null && ! "urn".equalsIgnoreCase(sectorIDURI.getScheme())) { 375 SectorID.ensureHTTPScheme(sectorIDURI); 376 SectorID.ensureHostComponent(sectorIDURI); 377 } 378 379 this.sectorIDURI = sectorIDURI; 380 } 381 382 383 /** 384 * Resolves the sector identifier from the client metadata. 385 * 386 * @return The sector identifier, {@code null} if the subject type is 387 * set to public. 388 * 389 * @throws IllegalStateException If resolution failed due to incomplete 390 * or inconsistent metadata. 391 */ 392 public SectorID resolveSectorID() { 393 394 if (! SubjectType.PAIRWISE.equals(getSubjectType())) { 395 // subject type is not pairwise or null 396 return null; 397 } 398 399 // Check sector identifier URI first 400 if (getSectorIDURI() != null) { 401 return new SectorID(getSectorIDURI()); 402 } 403 404 // Check redirect URIs second 405 if (CollectionUtils.isEmpty(getRedirectionURIs())) { 406 throw new IllegalStateException("Couldn't resolve sector ID: Missing redirect_uris"); 407 } 408 409 if (getRedirectionURIs().size() > 1) { 410 throw new IllegalStateException("Couldn't resolve sector ID: More than one redirect_uri, sector_identifier_uri not specified"); 411 } 412 413 return new SectorID(getRedirectionURIs().iterator().next()); 414 } 415 416 417 /** 418 * Gets the JSON Web Signature (JWS) algorithm required for the ID 419 * Tokens issued to this client. Corresponds to the 420 * {@code id_token_signed_response_alg} client metadata field. 421 * 422 * @return The JWS algorithm, {@code null} if not specified. 423 */ 424 public JWSAlgorithm getIDTokenJWSAlg() { 425 426 return idTokenJWSAlg; 427 } 428 429 430 /** 431 * Sets the JSON Web Signature (JWS) algorithm required for the ID 432 * Tokens issued to this client. Corresponds to the 433 * {@code id_token_signed_response_alg} client metadata field. 434 * 435 * @param idTokenJWSAlg The JWS algorithm, {@code null} if not 436 * specified. 437 */ 438 public void setIDTokenJWSAlg(final JWSAlgorithm idTokenJWSAlg) { 439 440 this.idTokenJWSAlg = idTokenJWSAlg; 441 } 442 443 444 /** 445 * Gets the JSON Web Encryption (JWE) algorithm required for the ID 446 * Tokens issued to this client. Corresponds to the 447 * {@code id_token_encrypted_response_alg} client metadata field. 448 * 449 * @return The JWE algorithm, {@code null} if not specified. 450 */ 451 public JWEAlgorithm getIDTokenJWEAlg() { 452 453 return idTokenJWEAlg; 454 } 455 456 457 /** 458 * Sets the JSON Web Encryption (JWE) algorithm required for the ID 459 * Tokens issued to this client. Corresponds to the 460 * {@code id_token_encrypted_response_alg} client metadata field. 461 * 462 * @param idTokenJWEAlg The JWE algorithm, {@code null} if not 463 * specified. 464 */ 465 public void setIDTokenJWEAlg(final JWEAlgorithm idTokenJWEAlg) { 466 467 this.idTokenJWEAlg = idTokenJWEAlg; 468 } 469 470 471 /** 472 * Gets the JSON Web Encryption (JWE) method required for the ID Tokens 473 * issued to this client. Corresponds to the 474 * {@code id_token_encrypted_response_enc} client metadata field. 475 * 476 * @return The JWE method, {@code null} if not specified. 477 */ 478 public EncryptionMethod getIDTokenJWEEnc() { 479 480 return idTokenJWEEnc; 481 } 482 483 484 /** 485 * Sets the JSON Web Encryption (JWE) method required for the ID Tokens 486 * issued to this client. Corresponds to the 487 * {@code id_token_encrypted_response_enc} client metadata field. 488 * 489 * @param idTokenJWEEnc The JWE method, {@code null} if not specified. 490 */ 491 public void setIDTokenJWEEnc(final EncryptionMethod idTokenJWEEnc) { 492 493 this.idTokenJWEEnc = idTokenJWEEnc; 494 } 495 496 497 /** 498 * Gets the JSON Web Signature (JWS) algorithm required for the 499 * UserInfo responses to this client. Corresponds to the 500 * {@code userinfo_signed_response_alg} client metadata field. 501 * 502 * @return The JWS algorithm, {@code null} if not specified. 503 */ 504 public JWSAlgorithm getUserInfoJWSAlg() { 505 506 return userInfoJWSAlg; 507 } 508 509 510 /** 511 * Sets the JSON Web Signature (JWS) algorithm required for the 512 * UserInfo responses to this client. Corresponds to the 513 * {@code userinfo_signed_response_alg} client metadata field. 514 * 515 * @param userInfoJWSAlg The JWS algorithm, {@code null} if not 516 * specified. 517 */ 518 public void setUserInfoJWSAlg(final JWSAlgorithm userInfoJWSAlg) { 519 520 this.userInfoJWSAlg = userInfoJWSAlg; 521 } 522 523 524 /** 525 * Gets the JSON Web Encryption (JWE) algorithm required for the 526 * UserInfo responses to this client. Corresponds to the 527 * {@code userinfo_encrypted_response_alg} client metadata field. 528 * 529 * @return The JWE algorithm, {@code null} if not specified. 530 */ 531 public JWEAlgorithm getUserInfoJWEAlg() { 532 533 return userInfoJWEAlg; 534 } 535 536 537 /** 538 * Sets the JSON Web Encryption (JWE) algorithm required for the 539 * UserInfo responses to this client. Corresponds to the 540 * {@code userinfo_encrypted_response_alg} client metadata field. 541 * 542 * @param userInfoJWEAlg The JWE algorithm, {@code null} if not 543 * specified. 544 */ 545 public void setUserInfoJWEAlg(final JWEAlgorithm userInfoJWEAlg) { 546 547 this.userInfoJWEAlg = userInfoJWEAlg; 548 } 549 550 551 /** 552 * Gets the JSON Web Encryption (JWE) method required for the UserInfo 553 * responses to this client. Corresponds to the 554 * {@code userinfo_encrypted_response_enc} client metadata field. 555 * 556 * @return The JWE method, {@code null} if not specified. 557 */ 558 public EncryptionMethod getUserInfoJWEEnc() { 559 560 return userInfoJWEEnc; 561 } 562 563 564 /** 565 * Sets the JSON Web Encryption (JWE) method required for the UserInfo 566 * responses to this client. Corresponds to the 567 * {@code userinfo_encrypted_response_enc} client metadata field. 568 * 569 * @param userInfoJWEEnc The JWE method, {@code null} if not specified. 570 */ 571 public void setUserInfoJWEEnc(final EncryptionMethod userInfoJWEEnc) { 572 573 this.userInfoJWEEnc = userInfoJWEEnc; 574 } 575 576 577 /** 578 * Gets the default maximum authentication age. Corresponds to the 579 * {@code default_max_age} client metadata field. 580 * 581 * @return The default max authentication age, in seconds. If not 582 * specified -1. 583 */ 584 public int getDefaultMaxAge() { 585 586 return defaultMaxAge; 587 } 588 589 590 /** 591 * Sets the default maximum authentication age. Corresponds to the 592 * {@code default_max_age} client metadata field. 593 * 594 * @param defaultMaxAge The default max authentication age, in seconds. 595 * If not specified -1. 596 */ 597 public void setDefaultMaxAge(final int defaultMaxAge) { 598 599 this.defaultMaxAge = defaultMaxAge; 600 } 601 602 603 /** 604 * Gets the default requirement for the {@code auth_time} claim in the 605 * ID Token. Corresponds to the {@code require_auth_time} client 606 * metadata field. 607 * 608 * @return If {@code true} the {@code auth_Time} claim in the ID Token 609 * is required by default. 610 */ 611 public boolean requiresAuthTime() { 612 613 return requiresAuthTime; 614 } 615 616 617 /** 618 * Sets the default requirement for the {@code auth_time} claim in the 619 * ID Token. Corresponds to the {@code require_auth_time} client 620 * metadata field. 621 * 622 * @param requiresAuthTime If {@code true} the {@code auth_Time} claim 623 * in the ID Token is required by default. 624 */ 625 public void requiresAuthTime(final boolean requiresAuthTime) { 626 627 this.requiresAuthTime = requiresAuthTime; 628 } 629 630 631 /** 632 * Gets the default Authentication Context Class Reference (ACR) 633 * values. Corresponds to the {@code default_acr_values} client 634 * metadata field. 635 * 636 * @return The default ACR values, by order of preference, 637 * {@code null} if not specified. 638 */ 639 public List<ACR> getDefaultACRs() { 640 641 return defaultACRs; 642 } 643 644 645 /** 646 * Sets the default Authentication Context Class Reference (ACR) 647 * values. Corresponds to the {@code default_acr_values} client 648 * metadata field. 649 * 650 * @param defaultACRs The default ACRs, by order of preference, 651 * {@code null} if not specified. 652 */ 653 public void setDefaultACRs(final List<ACR> defaultACRs) { 654 655 this.defaultACRs = defaultACRs; 656 } 657 658 659 /** 660 * Gets the HTTPS URI that the authorisation server can call to 661 * initiate a login at the client. Corresponds to the 662 * {@code initiate_login_uri} client metadata field. 663 * 664 * @return The login URI, {@code null} if not specified. 665 */ 666 public URI getInitiateLoginURI() { 667 668 return initiateLoginURI; 669 } 670 671 672 /** 673 * Sets the HTTPS URI that the authorisation server can call to 674 * initiate a login at the client. Corresponds to the 675 * {@code initiate_login_uri} client metadata field. 676 * 677 * @param loginURI The login URI, {@code null} if not specified. The 678 * URI scheme must be https. 679 */ 680 public void setInitiateLoginURI(final URI loginURI) { 681 682 URIUtils.ensureSchemeIsHTTPS(loginURI); 683 this.initiateLoginURI = loginURI; 684 } 685 686 687 /** 688 * Gets the post logout redirection URIs. Corresponds to the 689 * {@code post_logout_redirect_uris} client metadata field. 690 * 691 * @return The logout redirection URIs, {@code null} if not specified. 692 */ 693 public Set<URI> getPostLogoutRedirectionURIs() { 694 695 return postLogoutRedirectURIs; 696 } 697 698 699 /** 700 * Sets the post logout redirection URIs. Corresponds to the 701 * {@code post_logout_redirect_uris} client metadata field. 702 * 703 * @param logoutURIs The post logout redirection URIs, {@code null} if 704 * not specified. 705 */ 706 public void setPostLogoutRedirectionURIs(final Set<URI> logoutURIs) { 707 708 if (logoutURIs != null) { 709 for (URI uri: logoutURIs) { 710 URIUtils.ensureSchemeIsNotProhibited(uri, PROHIBITED_REDIRECT_URI_SCHEMES); 711 } 712 } 713 postLogoutRedirectURIs = logoutURIs; 714 } 715 716 717 /** 718 * Gets the front-channel logout URI. Corresponds to the 719 * {@code frontchannel_logout_uri} client metadata field. 720 * 721 * @return The front-channel logout URI, {@code null} if not specified. 722 */ 723 public URI getFrontChannelLogoutURI() { 724 725 return frontChannelLogoutURI; 726 } 727 728 729 /** 730 * Sets the front-channel logout URI. Corresponds to the 731 * {@code frontchannel_logout_uri} client metadata field. 732 * 733 * @param frontChannelLogoutURI The front-channel logout URI, 734 * {@code null} if not specified. 735 */ 736 public void setFrontChannelLogoutURI(final URI frontChannelLogoutURI) { 737 738 if (frontChannelLogoutURI != null && frontChannelLogoutURI.getScheme() == null) { 739 throw new IllegalArgumentException("Missing URI scheme"); 740 } 741 742 this.frontChannelLogoutURI = frontChannelLogoutURI; 743 } 744 745 746 /** 747 * Gets the requirement for a session identifier on front-channel 748 * logout. Corresponds to 749 * the {@code frontchannel_logout_session_required} client metadata 750 * field. 751 * 752 * @return {@code true} if a session identifier is required, else 753 * {@code false}. 754 */ 755 public boolean requiresFrontChannelLogoutSession() { 756 757 return frontChannelLogoutSessionRequired; 758 } 759 760 761 /** 762 * Sets the requirement for a session identifier on front-channel 763 * logout. Corresponds to 764 * the {@code frontchannel_logout_session_required} client metadata 765 * field. 766 * 767 * @param requiresSession {@code true} if a session identifier is 768 * required, else {@code false}. 769 */ 770 public void requiresFrontChannelLogoutSession(boolean requiresSession) { 771 772 frontChannelLogoutSessionRequired = requiresSession; 773 } 774 775 776 /** 777 * Gets the back-channel logout URI. Corresponds to the 778 * {@code backchannel_logout_uri} client metadata field. 779 * 780 * @return The back-channel logout URI, {@code null} if not specified. 781 */ 782 public URI getBackChannelLogoutURI() { 783 784 return backChannelLogoutURI; 785 } 786 787 788 /** 789 * Sets the back-channel logout URI. Corresponds to the 790 * {@code backchannel_logout_uri} client metadata field. 791 * 792 * @param backChannelLogoutURI The back-channel logout URI, 793 * {@code null} if not specified. The URI 794 * scheme must be https or http. 795 */ 796 public void setBackChannelLogoutURI(final URI backChannelLogoutURI) { 797 798 URIUtils.ensureSchemeIsHTTPSorHTTP(backChannelLogoutURI); 799 this.backChannelLogoutURI = backChannelLogoutURI; 800 } 801 802 803 /** 804 * Gets the requirement for a session identifier on back-channel 805 * logout. Corresponds to 806 * the {@code backchannel_logout_session_required} client metadata 807 * field. 808 * 809 * @return {@code true} if a session identifier is required, else 810 * {@code false}. 811 */ 812 public boolean requiresBackChannelLogoutSession() { 813 814 return backChannelLogoutSessionRequired; 815 } 816 817 818 /** 819 * Sets the requirement for a session identifier on back-channel 820 * logout. Corresponds to 821 * the {@code backchannel_logout_session_required} client metadata 822 * field. 823 * 824 * @param requiresSession {@code true} if a session identifier is 825 * required, else {@code false}. 826 */ 827 public void requiresBackChannelLogoutSession(final boolean requiresSession) { 828 829 backChannelLogoutSessionRequired = requiresSession; 830 } 831 832 833 /** 834 * Gets the digest algorithm for the external evidence attachments in 835 * OpenID Connect for Identity Assurance 1.0. Corresponds to the 836 * {@code digest_algorithm} client metadata field. 837 * 838 * @return The digest algorithm, {@code null} if not specified. 839 */ 840 public HashAlgorithm getAttachmentDigestAlg() { 841 842 return attachmentDigestAlg; 843 } 844 845 846 /** 847 * Sets the digest algorithm for the external evidence attachments in 848 * OpenID Connect for Identity Assurance 1.0. Corresponds to the 849 * {@code digest_algorithm} client metadata field. 850 * 851 * @param hashAlg The digest algorithm, {@code null} if not specified. 852 */ 853 public void setAttachmentDigestAlg(final HashAlgorithm hashAlg) { 854 855 attachmentDigestAlg = hashAlg; 856 } 857 858 859 /** 860 * Applies the client metadata defaults where no values have been 861 * specified. 862 * 863 * <ul> 864 * <li>The response types default to {@code ["code"]}. 865 * <li>The grant types default to {@code "authorization_code".} 866 * <li>The client authentication method defaults to 867 * "client_secret_basic". 868 * <li>The application type defaults to 869 * {@link ApplicationType#WEB}. 870 * <li>The ID token JWS algorithm defaults to "RS256". 871 * </ul> 872 */ 873 @Override 874 public void applyDefaults() { 875 876 super.applyDefaults(); 877 878 if (applicationType == null) { 879 applicationType = ApplicationType.WEB; 880 } 881 882 if (idTokenJWSAlg == null) { 883 idTokenJWSAlg = JWSAlgorithm.RS256; 884 } 885 } 886 887 888 @Override 889 public JSONObject toJSONObject(boolean includeCustomFields) { 890 891 JSONObject o = super.toJSONObject(includeCustomFields); 892 893 o.putAll(getCustomFields()); 894 895 if (applicationType != null) 896 o.put("application_type", applicationType.toString()); 897 898 if (subjectType != null) 899 o.put("subject_type", subjectType.toString()); 900 901 902 if (sectorIDURI != null) 903 o.put("sector_identifier_uri", sectorIDURI.toString()); 904 905 906 if (idTokenJWSAlg != null) 907 o.put("id_token_signed_response_alg", idTokenJWSAlg.getName()); 908 909 910 if (idTokenJWEAlg != null) 911 o.put("id_token_encrypted_response_alg", idTokenJWEAlg.getName()); 912 913 914 if (idTokenJWEEnc != null) 915 o.put("id_token_encrypted_response_enc", idTokenJWEEnc.getName()); 916 917 918 if (userInfoJWSAlg != null) 919 o.put("userinfo_signed_response_alg", userInfoJWSAlg.getName()); 920 921 922 if (userInfoJWEAlg != null) 923 o.put("userinfo_encrypted_response_alg", userInfoJWEAlg.getName()); 924 925 926 if (userInfoJWEEnc != null) 927 o.put("userinfo_encrypted_response_enc", userInfoJWEEnc.getName()); 928 929 930 if (defaultMaxAge > 0) 931 o.put("default_max_age", defaultMaxAge); 932 933 934 if (requiresAuthTime()) 935 o.put("require_auth_time", requiresAuthTime); 936 937 938 if (defaultACRs != null) { 939 940 JSONArray acrList = new JSONArray(); 941 942 for (ACR acr: defaultACRs) { 943 acrList.add(acr.getValue()); 944 } 945 o.put("default_acr_values", acrList); 946 } 947 948 949 if (initiateLoginURI != null) 950 o.put("initiate_login_uri", initiateLoginURI.toString()); 951 952 953 if (postLogoutRedirectURIs != null) { 954 955 JSONArray uriList = new JSONArray(); 956 957 for (URI uri: postLogoutRedirectURIs) 958 uriList.add(uri.toString()); 959 960 o.put("post_logout_redirect_uris", uriList); 961 } 962 963 if (frontChannelLogoutURI != null) { 964 o.put("frontchannel_logout_uri", frontChannelLogoutURI.toString()); 965 o.put("frontchannel_logout_session_required", frontChannelLogoutSessionRequired); 966 } 967 968 if (backChannelLogoutURI != null) { 969 o.put("backchannel_logout_uri", backChannelLogoutURI.toString()); 970 o.put("backchannel_logout_session_required", backChannelLogoutSessionRequired); 971 } 972 973 if (attachmentDigestAlg != null) { 974 o.put("digest_algorithm", attachmentDigestAlg.getValue()); 975 } 976 977 return o; 978 } 979 980 981 /** 982 * Parses an OpenID Connect client metadata instance from the specified 983 * JSON object. 984 * 985 * @param jsonObject The JSON object to parse. Must not be 986 * {@code null}. 987 * 988 * @return The OpenID Connect client metadata. 989 * 990 * @throws ParseException If the JSON object couldn't be parsed to an 991 * OpenID Connect client metadata instance. 992 */ 993 public static OIDCClientMetadata parse(final JSONObject jsonObject) 994 throws ParseException { 995 996 ClientMetadata baseMetadata = ClientMetadata.parse(jsonObject); 997 998 OIDCClientMetadata metadata = new OIDCClientMetadata(baseMetadata); 999 1000 // Parse the OIDC-specific fields from the custom OAuth 2.0 dyn 1001 // reg fields 1002 1003 JSONObject oidcFields = baseMetadata.getCustomFields(); 1004 1005 try { 1006 if (jsonObject.get("application_type") != null) { 1007 metadata.setApplicationType(JSONObjectUtils.getEnum(jsonObject, "application_type", ApplicationType.class)); 1008 oidcFields.remove("application_type"); 1009 } 1010 1011 if (jsonObject.get("subject_type") != null) { 1012 metadata.setSubjectType(JSONObjectUtils.getEnum(jsonObject, "subject_type", SubjectType.class)); 1013 oidcFields.remove("subject_type"); 1014 } 1015 1016 if (jsonObject.get("sector_identifier_uri") != null) { 1017 try { 1018 metadata.setSectorIDURI(JSONObjectUtils.getURI(jsonObject, "sector_identifier_uri")); 1019 } catch (IllegalArgumentException e) { 1020 throw new ParseException("Invalid sector_identifier_uri parameter: " + e.getMessage()); 1021 } 1022 oidcFields.remove("sector_identifier_uri"); 1023 } 1024 1025 if (jsonObject.get("id_token_signed_response_alg") != null) { 1026 metadata.setIDTokenJWSAlg(JWSAlgorithm.parse( 1027 JSONObjectUtils.getString(jsonObject, "id_token_signed_response_alg"))); 1028 1029 oidcFields.remove("id_token_signed_response_alg"); 1030 } 1031 1032 if (jsonObject.get("id_token_encrypted_response_alg") != null) { 1033 metadata.setIDTokenJWEAlg(JWEAlgorithm.parse( 1034 JSONObjectUtils.getString(jsonObject, "id_token_encrypted_response_alg"))); 1035 1036 oidcFields.remove("id_token_encrypted_response_alg"); 1037 } 1038 1039 if (jsonObject.get("id_token_encrypted_response_enc") != null) { 1040 metadata.setIDTokenJWEEnc(EncryptionMethod.parse( 1041 JSONObjectUtils.getString(jsonObject, "id_token_encrypted_response_enc"))); 1042 1043 oidcFields.remove("id_token_encrypted_response_enc"); 1044 } 1045 1046 if (jsonObject.get("userinfo_signed_response_alg") != null) { 1047 metadata.setUserInfoJWSAlg(JWSAlgorithm.parse( 1048 JSONObjectUtils.getString(jsonObject, "userinfo_signed_response_alg"))); 1049 1050 oidcFields.remove("userinfo_signed_response_alg"); 1051 } 1052 1053 if (jsonObject.get("userinfo_encrypted_response_alg") != null) { 1054 metadata.setUserInfoJWEAlg(JWEAlgorithm.parse( 1055 JSONObjectUtils.getString(jsonObject, "userinfo_encrypted_response_alg"))); 1056 1057 oidcFields.remove("userinfo_encrypted_response_alg"); 1058 } 1059 1060 if (jsonObject.get("userinfo_encrypted_response_enc") != null) { 1061 metadata.setUserInfoJWEEnc(EncryptionMethod.parse( 1062 JSONObjectUtils.getString(jsonObject, "userinfo_encrypted_response_enc"))); 1063 1064 oidcFields.remove("userinfo_encrypted_response_enc"); 1065 } 1066 1067 if (jsonObject.get("default_max_age") != null) { 1068 metadata.setDefaultMaxAge(JSONObjectUtils.getInt(jsonObject, "default_max_age")); 1069 oidcFields.remove("default_max_age"); 1070 } 1071 1072 if (jsonObject.get("require_auth_time") != null) { 1073 metadata.requiresAuthTime(JSONObjectUtils.getBoolean(jsonObject, "require_auth_time")); 1074 oidcFields.remove("require_auth_time"); 1075 } 1076 1077 if (jsonObject.get("default_acr_values") != null) { 1078 1079 List<ACR> acrValues = new LinkedList<>(); 1080 1081 for (String acrString : JSONObjectUtils.getStringArray(jsonObject, "default_acr_values")) 1082 acrValues.add(new ACR(acrString)); 1083 1084 metadata.setDefaultACRs(acrValues); 1085 1086 oidcFields.remove("default_acr_values"); 1087 } 1088 1089 if (jsonObject.get("initiate_login_uri") != null) { 1090 try { 1091 metadata.setInitiateLoginURI(JSONObjectUtils.getURI(jsonObject, "initiate_login_uri")); 1092 } catch (IllegalArgumentException e) { 1093 throw new ParseException("Invalid initiate_login_uri parameter: " + e.getMessage()); 1094 } 1095 oidcFields.remove("initiate_login_uri"); 1096 } 1097 1098 if (jsonObject.get("post_logout_redirect_uris") != null) { 1099 1100 Set<URI> logoutURIs = new LinkedHashSet<>(); 1101 1102 for (String uriString : JSONObjectUtils.getStringArray(jsonObject, "post_logout_redirect_uris")) { 1103 1104 try { 1105 logoutURIs.add(new URI(uriString)); 1106 } catch (URISyntaxException e) { 1107 throw new ParseException("Invalid post_logout_redirect_uris parameter"); 1108 } 1109 } 1110 1111 try { 1112 metadata.setPostLogoutRedirectionURIs(logoutURIs); 1113 } catch (IllegalArgumentException e) { 1114 throw new ParseException("Invalid post_logout_redirect_uris parameter: " + e.getMessage()); 1115 } 1116 oidcFields.remove("post_logout_redirect_uris"); 1117 } 1118 1119 if (jsonObject.get("frontchannel_logout_uri") != null) { 1120 1121 try { 1122 metadata.setFrontChannelLogoutURI(JSONObjectUtils.getURI(jsonObject, "frontchannel_logout_uri")); 1123 } catch (IllegalArgumentException e) { 1124 throw new ParseException("Invalid frontchannel_logout_uri parameter: " + e.getMessage()); 1125 } 1126 oidcFields.remove("frontchannel_logout_uri"); 1127 1128 if (jsonObject.get("frontchannel_logout_session_required") != null) { 1129 metadata.requiresFrontChannelLogoutSession(JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_session_required")); 1130 oidcFields.remove("frontchannel_logout_session_required"); 1131 } 1132 } 1133 1134 1135 if (jsonObject.get("backchannel_logout_uri") != null) { 1136 1137 try { 1138 metadata.setBackChannelLogoutURI(JSONObjectUtils.getURI(jsonObject, "backchannel_logout_uri")); 1139 } catch (IllegalArgumentException e) { 1140 throw new ParseException("Invalid backchannel_logout_uri parameter: " + e.getMessage()); 1141 } 1142 oidcFields.remove("backchannel_logout_uri"); 1143 1144 if (jsonObject.get("backchannel_logout_session_required") != null) { 1145 metadata.requiresBackChannelLogoutSession(JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_session_required")); 1146 oidcFields.remove("backchannel_logout_session_required"); 1147 } 1148 } 1149 1150 if (jsonObject.get("digest_algorithm") != null) { 1151 metadata.setAttachmentDigestAlg(new HashAlgorithm(JSONObjectUtils.getString(jsonObject, "digest_algorithm"))); 1152 oidcFields.remove("digest_algorithm"); 1153 } 1154 1155 } catch (ParseException e) { 1156 // Insert client_client_metadata error code so that it 1157 // can be reported back to the client if we have a 1158 // registration event 1159 throw new ParseException(e.getMessage(), RegistrationError.INVALID_CLIENT_METADATA.appendDescription(": " + e.getMessage()), e.getCause()); 1160 } 1161 1162 // The remaining fields are custom 1163 metadata.setCustomFields(oidcFields); 1164 1165 return metadata; 1166 } 1167}