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