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