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