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