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