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.op; 019 020 021import java.io.IOException; 022import java.net.MalformedURLException; 023import java.net.URI; 024import java.net.URL; 025import java.util.*; 026 027import com.nimbusds.jose.EncryptionMethod; 028import com.nimbusds.jose.JWEAlgorithm; 029import com.nimbusds.jose.JWSAlgorithm; 030import com.nimbusds.langtag.LangTag; 031import com.nimbusds.langtag.LangTagException; 032import com.nimbusds.oauth2.sdk.GeneralException; 033import com.nimbusds.oauth2.sdk.ParseException; 034import com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata; 035import com.nimbusds.oauth2.sdk.http.HTTPRequest; 036import com.nimbusds.oauth2.sdk.http.HTTPResponse; 037import com.nimbusds.oauth2.sdk.id.Issuer; 038import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 039import com.nimbusds.openid.connect.sdk.Display; 040import com.nimbusds.openid.connect.sdk.SubjectType; 041import com.nimbusds.openid.connect.sdk.claims.ACR; 042import com.nimbusds.openid.connect.sdk.claims.ClaimType; 043import net.minidev.json.JSONObject; 044 045 046/** 047 * OpenID Provider (OP) metadata. 048 * 049 * <p>Related specifications: 050 * 051 * <ul> 052 * <li>OpenID Connect Discovery 1.0, section 3. 053 * <li>OpenID Connect Session Management 1.0, section 2.1 (draft 28). 054 * <li>OpenID Connect Front-Channel Logout 1.0, section 3 (draft 02). 055 * <li>OpenID Connect Back-Channel Logout 1.0, section 2.1 (draft 04). 056 * <li>OAuth 2.0 Authorization Server Metadata (RFC 8414) 057 * <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound 058 * Access Tokens (draft-ietf-oauth-mtls-08) 059 * </ul> 060 */ 061public class OIDCProviderMetadata extends AuthorizationServerMetadata { 062 063 064 /** 065 * The registered parameter names. 066 */ 067 private static final Set<String> REGISTERED_PARAMETER_NAMES; 068 069 070 static { 071 Set<String> p = new HashSet<>(AuthorizationServerMetadata.getRegisteredParameterNames()); 072 p.add("userinfo_endpoint"); 073 p.add("check_session_iframe"); 074 p.add("end_session_endpoint"); 075 p.add("acr_values_supported"); 076 p.add("subject_types_supported"); 077 p.add("id_token_signing_alg_values_supported"); 078 p.add("id_token_encryption_alg_values_supported"); 079 p.add("id_token_encryption_enc_values_supported"); 080 p.add("userinfo_signing_alg_values_supported"); 081 p.add("userinfo_encryption_alg_values_supported"); 082 p.add("userinfo_encryption_enc_values_supported"); 083 p.add("display_values_supported"); 084 p.add("claim_types_supported"); 085 p.add("claims_supported"); 086 p.add("claims_locales_supported"); 087 p.add("claims_parameter_supported"); 088 p.add("request_parameter_supported"); 089 p.add("request_uri_parameter_supported"); 090 p.add("require_request_uri_registration"); 091 p.add("backchannel_logout_supported"); 092 p.add("backchannel_logout_session_supported"); 093 p.add("frontchannel_logout_supported"); 094 p.add("frontchannel_logout_session_supported"); 095 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 096 } 097 098 099 /** 100 * The UserInfo endpoint. 101 */ 102 private URI userInfoEndpoint; 103 104 105 /** 106 * The cross-origin check session iframe. 107 */ 108 private URI checkSessionIframe; 109 110 111 /** 112 * The logout endpoint. 113 */ 114 private URI endSessionEndpoint; 115 116 117 /** 118 * The supported ACRs. 119 */ 120 private List<ACR> acrValues; 121 122 123 /** 124 * The supported subject types. 125 */ 126 private final List<SubjectType> subjectTypes; 127 128 129 /** 130 * The supported ID token JWS algorithms. 131 */ 132 private List<JWSAlgorithm> idTokenJWSAlgs; 133 134 135 /** 136 * The supported ID token JWE algorithms. 137 */ 138 private List<JWEAlgorithm> idTokenJWEAlgs; 139 140 141 /** 142 * The supported ID token encryption methods. 143 */ 144 private List<EncryptionMethod> idTokenJWEEncs; 145 146 147 /** 148 * The supported UserInfo JWS algorithms. 149 */ 150 private List<JWSAlgorithm> userInfoJWSAlgs; 151 152 153 /** 154 * The supported UserInfo JWE algorithms. 155 */ 156 private List<JWEAlgorithm> userInfoJWEAlgs; 157 158 159 /** 160 * The supported UserInfo encryption methods. 161 */ 162 private List<EncryptionMethod> userInfoJWEEncs; 163 164 165 /** 166 * The supported displays. 167 */ 168 private List<Display> displays; 169 170 171 /** 172 * The supported claim types. 173 */ 174 private List<ClaimType> claimTypes; 175 176 177 /** 178 * The supported claims names. 179 */ 180 private List<String> claims; 181 182 183 /** 184 * The supported claims locales. 185 */ 186 private List<LangTag> claimsLocales; 187 188 189 /** 190 * If {@code true} the {@code claims} parameter is supported, else not. 191 */ 192 private boolean claimsParamSupported = false; 193 194 195 /** 196 * If {@code true} the {@code frontchannel_logout_supported} parameter 197 * is set, else not. 198 */ 199 private boolean frontChannelLogoutSupported = false; 200 201 202 /** 203 * If {@code true} the {@code frontchannel_logout_session_supported} 204 * parameter is set, else not. 205 */ 206 private boolean frontChannelLogoutSessionSupported = false; 207 208 209 /** 210 * If {@code true} the {@code backchannel_logout_supported} parameter 211 * is set, else not. 212 */ 213 private boolean backChannelLogoutSupported = false; 214 215 216 /** 217 * If {@code true} the {@code backchannel_logout_session_supported} 218 * parameter is set, else not. 219 */ 220 private boolean backChannelLogoutSessionSupported = false; 221 222 223 /** 224 * Creates a new OpenID Connect provider metadata instance. 225 * 226 * @param issuer The issuer identifier. Must be an URI using the 227 * https scheme with no query or fragment 228 * component. Must not be {@code null}. 229 * @param subjectTypes The supported subject types. At least one must 230 * be specified. Must not be {@code null}. 231 * @param jwkSetURI The JWK set URI. Must not be {@code null}. 232 */ 233 public OIDCProviderMetadata(final Issuer issuer, 234 final List<SubjectType> subjectTypes, 235 final URI jwkSetURI) { 236 237 super(issuer); 238 239 if (subjectTypes.size() < 1) 240 throw new IllegalArgumentException("At least one supported subject type must be specified"); 241 242 this.subjectTypes = subjectTypes; 243 244 if (jwkSetURI == null) 245 throw new IllegalArgumentException("The public JWK set URI must not be null"); 246 247 setJWKSetURI(jwkSetURI); 248 } 249 250 251 /** 252 * Gets the registered OpenID Connect provider metadata parameter 253 * names. 254 * 255 * @return The registered OpenID Connect provider metadata parameter 256 * names, as an unmodifiable set. 257 */ 258 public static Set<String> getRegisteredParameterNames() { 259 260 return REGISTERED_PARAMETER_NAMES; 261 } 262 263 264 /** 265 * Gets the UserInfo endpoint URI. Corresponds the 266 * {@code userinfo_endpoint} metadata field. 267 * 268 * @return The UserInfo endpoint URI, {@code null} if not specified. 269 */ 270 public URI getUserInfoEndpointURI() { 271 272 return userInfoEndpoint; 273 } 274 275 276 /** 277 * Sets the UserInfo endpoint URI. Corresponds the 278 * {@code userinfo_endpoint} metadata field. 279 * 280 * @param userInfoEndpoint The UserInfo endpoint URI, {@code null} if 281 * not specified. 282 */ 283 public void setUserInfoEndpointURI(final URI userInfoEndpoint) { 284 285 this.userInfoEndpoint = userInfoEndpoint; 286 } 287 288 289 /** 290 * Gets the cross-origin check session iframe URI. Corresponds to the 291 * {@code check_session_iframe} metadata field. 292 * 293 * @return The check session iframe URI, {@code null} if not specified. 294 */ 295 public URI getCheckSessionIframeURI() { 296 297 return checkSessionIframe; 298 } 299 300 301 /** 302 * Sets the cross-origin check session iframe URI. Corresponds to the 303 * {@code check_session_iframe} metadata field. 304 * 305 * @param checkSessionIframe The check session iframe URI, {@code null} 306 * if not specified. 307 */ 308 public void setCheckSessionIframeURI(final URI checkSessionIframe) { 309 310 this.checkSessionIframe = checkSessionIframe; 311 } 312 313 314 /** 315 * Gets the logout endpoint URI. Corresponds to the 316 * {@code end_session_endpoint} metadata field. 317 * 318 * @return The logoout endpoint URI, {@code null} if not specified. 319 */ 320 public URI getEndSessionEndpointURI() { 321 322 return endSessionEndpoint; 323 } 324 325 326 /** 327 * Sets the logout endpoint URI. Corresponds to the 328 * {@code end_session_endpoint} metadata field. 329 * 330 * @param endSessionEndpoint The logoout endpoint URI, {@code null} if 331 * not specified. 332 */ 333 public void setEndSessionEndpointURI(final URI endSessionEndpoint) { 334 335 this.endSessionEndpoint = endSessionEndpoint; 336 } 337 338 /** 339 * Gets the supported Authentication Context Class References (ACRs). 340 * Corresponds to the {@code acr_values_supported} metadata field. 341 * 342 * @return The supported ACRs, {@code null} if not specified. 343 */ 344 public List<ACR> getACRs() { 345 346 return acrValues; 347 } 348 349 350 /** 351 * Sets the supported Authentication Context Class References (ACRs). 352 * Corresponds to the {@code acr_values_supported} metadata field. 353 * 354 * @param acrValues The supported ACRs, {@code null} if not specified. 355 */ 356 public void setACRs(final List<ACR> acrValues) { 357 358 this.acrValues = acrValues; 359 } 360 361 362 /** 363 * Gets the supported subject types. Corresponds to the 364 * {@code subject_types_supported} metadata field. 365 * 366 * @return The supported subject types. 367 */ 368 public List<SubjectType> getSubjectTypes() { 369 370 return subjectTypes; 371 } 372 373 374 /** 375 * Gets the supported JWS algorithms for ID tokens. Corresponds to the 376 * {@code id_token_signing_alg_values_supported} metadata field. 377 * 378 * @return The supported JWS algorithms, {@code null} if not specified. 379 */ 380 public List<JWSAlgorithm> getIDTokenJWSAlgs() { 381 382 return idTokenJWSAlgs; 383 } 384 385 386 /** 387 * Sets the supported JWS algorithms for ID tokens. Corresponds to the 388 * {@code id_token_signing_alg_values_supported} metadata field. 389 * 390 * @param idTokenJWSAlgs The supported JWS algorithms, {@code null} if 391 * not specified. 392 */ 393 public void setIDTokenJWSAlgs(final List<JWSAlgorithm> idTokenJWSAlgs) { 394 395 this.idTokenJWSAlgs = idTokenJWSAlgs; 396 } 397 398 399 /** 400 * Gets the supported JWE algorithms for ID tokens. Corresponds to the 401 * {@code id_token_encryption_alg_values_supported} metadata field. 402 * 403 * @return The supported JWE algorithms, {@code null} if not specified. 404 */ 405 public List<JWEAlgorithm> getIDTokenJWEAlgs() { 406 407 return idTokenJWEAlgs; 408 } 409 410 411 /** 412 * Sets the supported JWE algorithms for ID tokens. Corresponds to the 413 * {@code id_token_encryption_alg_values_supported} metadata field. 414 * 415 * @param idTokenJWEAlgs The supported JWE algorithms, {@code null} if 416 * not specified. 417 */ 418 public void setIDTokenJWEAlgs(final List<JWEAlgorithm> idTokenJWEAlgs) { 419 420 this.idTokenJWEAlgs = idTokenJWEAlgs; 421 } 422 423 424 /** 425 * Gets the supported encryption methods for ID tokens. Corresponds to 426 * the {@code id_token_encryption_enc_values_supported} metadata field. 427 * 428 * @return The supported encryption methods, {@code null} if not 429 * specified. 430 */ 431 public List<EncryptionMethod> getIDTokenJWEEncs() { 432 433 return idTokenJWEEncs; 434 } 435 436 437 /** 438 * Sets the supported encryption methods for ID tokens. Corresponds to 439 * the {@code id_token_encryption_enc_values_supported} metadata field. 440 * 441 * @param idTokenJWEEncs The supported encryption methods, {@code null} 442 * if not specified. 443 */ 444 public void setIDTokenJWEEncs(final List<EncryptionMethod> idTokenJWEEncs) { 445 446 this.idTokenJWEEncs = idTokenJWEEncs; 447 } 448 449 450 /** 451 * Gets the supported JWS algorithms for UserInfo JWTs. Corresponds to 452 * the {@code userinfo_signing_alg_values_supported} metadata field. 453 * 454 * @return The supported JWS algorithms, {@code null} if not specified. 455 */ 456 public List<JWSAlgorithm> getUserInfoJWSAlgs() { 457 458 return userInfoJWSAlgs; 459 } 460 461 462 /** 463 * Sets the supported JWS algorithms for UserInfo JWTs. Corresponds to 464 * the {@code userinfo_signing_alg_values_supported} metadata field. 465 * 466 * @param userInfoJWSAlgs The supported JWS algorithms, {@code null} if 467 * not specified. 468 */ 469 public void setUserInfoJWSAlgs(final List<JWSAlgorithm> userInfoJWSAlgs) { 470 471 this.userInfoJWSAlgs = userInfoJWSAlgs; 472 } 473 474 475 /** 476 * Gets the supported JWE algorithms for UserInfo JWTs. Corresponds to 477 * the {@code userinfo_encryption_alg_values_supported} metadata field. 478 * 479 * @return The supported JWE algorithms, {@code null} if not specified. 480 */ 481 public List<JWEAlgorithm> getUserInfoJWEAlgs() { 482 483 return userInfoJWEAlgs; 484 } 485 486 487 /** 488 * Sets the supported JWE algorithms for UserInfo JWTs. Corresponds to 489 * the {@code userinfo_encryption_alg_values_supported} metadata field. 490 * 491 * @param userInfoJWEAlgs The supported JWE algorithms, {@code null} if 492 * not specified. 493 */ 494 public void setUserInfoJWEAlgs(final List<JWEAlgorithm> userInfoJWEAlgs) { 495 496 this.userInfoJWEAlgs = userInfoJWEAlgs; 497 } 498 499 500 /** 501 * Gets the supported encryption methods for UserInfo JWTs. Corresponds 502 * to the {@code userinfo_encryption_enc_values_supported} metadata 503 * field. 504 * 505 * @return The supported encryption methods, {@code null} if not 506 * specified. 507 */ 508 public List<EncryptionMethod> getUserInfoJWEEncs() { 509 510 return userInfoJWEEncs; 511 } 512 513 514 /** 515 * Sets the supported encryption methods for UserInfo JWTs. Corresponds 516 * to the {@code userinfo_encryption_enc_values_supported} metadata 517 * field. 518 * 519 * @param userInfoJWEEncs The supported encryption methods, 520 * {@code null} if not specified. 521 */ 522 public void setUserInfoJWEEncs(final List<EncryptionMethod> userInfoJWEEncs) { 523 524 this.userInfoJWEEncs = userInfoJWEEncs; 525 } 526 527 528 /** 529 * Gets the supported displays. Corresponds to the 530 * {@code display_values_supported} metadata field. 531 * 532 * @return The supported displays, {@code null} if not specified. 533 */ 534 public List<Display> getDisplays() { 535 536 return displays; 537 } 538 539 540 /** 541 * Sets the supported displays. Corresponds to the 542 * {@code display_values_supported} metadata field. 543 * 544 * @param displays The supported displays, {@code null} if not 545 * specified. 546 */ 547 public void setDisplays(final List<Display> displays) { 548 549 this.displays = displays; 550 } 551 552 553 /** 554 * Gets the supported claim types. Corresponds to the 555 * {@code claim_types_supported} metadata field. 556 * 557 * @return The supported claim types, {@code null} if not specified. 558 */ 559 public List<ClaimType> getClaimTypes() { 560 561 return claimTypes; 562 } 563 564 565 /** 566 * Sets the supported claim types. Corresponds to the 567 * {@code claim_types_supported} metadata field. 568 * 569 * @param claimTypes The supported claim types, {@code null} if not 570 * specified. 571 */ 572 public void setClaimTypes(final List<ClaimType> claimTypes) { 573 574 this.claimTypes = claimTypes; 575 } 576 577 578 /** 579 * Gets the supported claims names. Corresponds to the 580 * {@code claims_supported} metadata field. 581 * 582 * @return The supported claims names, {@code null} if not specified. 583 */ 584 public List<String> getClaims() { 585 586 return claims; 587 } 588 589 590 /** 591 * Sets the supported claims names. Corresponds to the 592 * {@code claims_supported} metadata field. 593 * 594 * @param claims The supported claims names, {@code null} if not 595 * specified. 596 */ 597 public void setClaims(final List<String> claims) { 598 599 this.claims = claims; 600 } 601 602 603 /** 604 * Gets the supported claims locales. Corresponds to the 605 * {@code claims_locales_supported} metadata field. 606 * 607 * @return The supported claims locales, {@code null} if not specified. 608 */ 609 public List<LangTag> getClaimsLocales() { 610 611 return claimsLocales; 612 } 613 614 615 /** 616 * Sets the supported claims locales. Corresponds to the 617 * {@code claims_locales_supported} metadata field. 618 * 619 * @param claimsLocales The supported claims locales, {@code null} if 620 * not specified. 621 */ 622 public void setClaimLocales(final List<LangTag> claimsLocales) { 623 624 this.claimsLocales = claimsLocales; 625 } 626 627 628 /** 629 * Gets the support for the {@code claims} authorisation request 630 * parameter. Corresponds to the {@code claims_parameter_supported} 631 * metadata field. 632 * 633 * @return {@code true} if the {@code claim} parameter is supported, 634 * else {@code false}. 635 */ 636 public boolean supportsClaimsParam() { 637 638 return claimsParamSupported; 639 } 640 641 642 /** 643 * Sets the support for the {@code claims} authorisation request 644 * parameter. Corresponds to the {@code claims_parameter_supported} 645 * metadata field. 646 * 647 * @param claimsParamSupported {@code true} if the {@code claim} 648 * parameter is supported, else 649 * {@code false}. 650 */ 651 public void setSupportsClaimsParams(final boolean claimsParamSupported) { 652 653 this.claimsParamSupported = claimsParamSupported; 654 } 655 656 657 /** 658 * Gets the support for front-channel logout. Corresponds to the 659 * {@code frontchannel_logout_supported} metadata field. 660 * 661 * @return {@code true} if front-channel logout is supported, else 662 * {@code false}. 663 */ 664 public boolean supportsFrontChannelLogout() { 665 666 return frontChannelLogoutSupported; 667 } 668 669 670 /** 671 * Sets the support for front-channel logout. Corresponds to the 672 * {@code frontchannel_logout_supported} metadata field. 673 * 674 * @param frontChannelLogoutSupported {@code true} if front-channel 675 * logout is supported, else 676 * {@code false}. 677 */ 678 public void setSupportsFrontChannelLogout(final boolean frontChannelLogoutSupported) { 679 680 this.frontChannelLogoutSupported = frontChannelLogoutSupported; 681 } 682 683 684 /** 685 * Gets the support for front-channel logout with a session ID. 686 * Corresponds to the {@code frontchannel_logout_session_supported} 687 * metadata field. 688 * 689 * @return {@code true} if front-channel logout with a session ID is 690 * supported, else {@code false}. 691 */ 692 public boolean supportsFrontChannelLogoutSession() { 693 694 return frontChannelLogoutSessionSupported; 695 } 696 697 698 /** 699 * Sets the support for front-channel logout with a session ID. 700 * Corresponds to the {@code frontchannel_logout_session_supported} 701 * metadata field. 702 * 703 * @param frontChannelLogoutSessionSupported {@code true} if 704 * front-channel logout with 705 * a session ID is supported, 706 * else {@code false}. 707 */ 708 public void setSupportsFrontChannelLogoutSession(final boolean frontChannelLogoutSessionSupported) { 709 710 this.frontChannelLogoutSessionSupported = frontChannelLogoutSessionSupported; 711 } 712 713 714 /** 715 * Gets the support for back-channel logout. Corresponds to the 716 * {@code backchannel_logout_supported} metadata field. 717 * 718 * @return {@code true} if back-channel logout is supported, else 719 * {@code false}. 720 */ 721 public boolean supportsBackChannelLogout() { 722 723 return backChannelLogoutSupported; 724 } 725 726 727 /** 728 * Sets the support for back-channel logout. Corresponds to the 729 * {@code backchannel_logout_supported} metadata field. 730 * 731 * @param backChannelLogoutSupported {@code true} if back-channel 732 * logout is supported, else 733 * {@code false}. 734 */ 735 public void setSupportsBackChannelLogout(final boolean backChannelLogoutSupported) { 736 737 this.backChannelLogoutSupported = backChannelLogoutSupported; 738 } 739 740 741 /** 742 * Gets the support for back-channel logout with a session ID. 743 * Corresponds to the {@code backchannel_logout_session_supported} 744 * metadata field. 745 * 746 * @return {@code true} if back-channel logout with a session ID is 747 * supported, else {@code false}. 748 */ 749 public boolean supportsBackChannelLogoutSession() { 750 751 return backChannelLogoutSessionSupported; 752 } 753 754 755 /** 756 * Sets the support for back-channel logout with a session ID. 757 * Corresponds to the {@code backchannel_logout_session_supported} 758 * metadata field. 759 * 760 * @param backChannelLogoutSessionSupported {@code true} if 761 * back-channel logout with a 762 * session ID is supported, 763 * else {@code false}. 764 */ 765 public void setSupportsBackChannelLogoutSession(final boolean backChannelLogoutSessionSupported) { 766 767 this.backChannelLogoutSessionSupported = backChannelLogoutSessionSupported; 768 } 769 770 771 /** 772 * Applies the OpenID Provider metadata defaults where no values have 773 * been specified. 774 * 775 * <ul> 776 * <li>The response modes default to {@code ["query", "fragment"]}. 777 * <li>The grant types default to {@code ["authorization_code", 778 * "implicit"]}. 779 * <li>The token endpoint authentication methods default to 780 * {@code ["client_secret_basic"]}. 781 * <li>The claim types default to {@code ["normal]}. 782 * </ul> 783 */ 784 public void applyDefaults() { 785 786 super.applyDefaults(); 787 788 if (claimTypes == null) { 789 claimTypes = new ArrayList<>(1); 790 claimTypes.add(ClaimType.NORMAL); 791 } 792 } 793 794 795 /** 796 * Returns the JSON object representation of this OpenID Connect 797 * provider metadata. 798 * 799 * @return The JSON object representation. 800 */ 801 public JSONObject toJSONObject() { 802 803 JSONObject o = super.toJSONObject(); 804 805 // Mandatory fields 806 807 List<String> stringList = new ArrayList<>(subjectTypes.size()); 808 809 for (SubjectType st: subjectTypes) 810 stringList.add(st.toString()); 811 812 o.put("subject_types_supported", stringList); 813 814 // Optional fields 815 816 if (userInfoEndpoint != null) 817 o.put("userinfo_endpoint", userInfoEndpoint.toString()); 818 819 if (checkSessionIframe != null) 820 o.put("check_session_iframe", checkSessionIframe.toString()); 821 822 if (endSessionEndpoint != null) 823 o.put("end_session_endpoint", endSessionEndpoint.toString()); 824 825 if (acrValues != null) { 826 827 stringList = new ArrayList<>(acrValues.size()); 828 829 for (ACR acr: acrValues) 830 stringList.add(acr.getValue()); 831 832 o.put("acr_values_supported", stringList); 833 } 834 835 if (idTokenJWSAlgs != null) { 836 837 stringList = new ArrayList<>(idTokenJWSAlgs.size()); 838 839 for (JWSAlgorithm alg: idTokenJWSAlgs) 840 stringList.add(alg.getName()); 841 842 o.put("id_token_signing_alg_values_supported", stringList); 843 } 844 845 if (idTokenJWEAlgs != null) { 846 847 stringList = new ArrayList<>(idTokenJWEAlgs.size()); 848 849 for (JWEAlgorithm alg: idTokenJWEAlgs) 850 stringList.add(alg.getName()); 851 852 o.put("id_token_encryption_alg_values_supported", stringList); 853 } 854 855 if (idTokenJWEEncs != null) { 856 857 stringList = new ArrayList<>(idTokenJWEEncs.size()); 858 859 for (EncryptionMethod m: idTokenJWEEncs) 860 stringList.add(m.getName()); 861 862 o.put("id_token_encryption_enc_values_supported", stringList); 863 } 864 865 if (userInfoJWSAlgs != null) { 866 867 stringList = new ArrayList<>(userInfoJWSAlgs.size()); 868 869 for (JWSAlgorithm alg: userInfoJWSAlgs) 870 stringList.add(alg.getName()); 871 872 o.put("userinfo_signing_alg_values_supported", stringList); 873 } 874 875 if (userInfoJWEAlgs != null) { 876 877 stringList = new ArrayList<>(userInfoJWEAlgs.size()); 878 879 for (JWEAlgorithm alg: userInfoJWEAlgs) 880 stringList.add(alg.getName()); 881 882 o.put("userinfo_encryption_alg_values_supported", stringList); 883 } 884 885 if (userInfoJWEEncs != null) { 886 887 stringList = new ArrayList<>(userInfoJWEEncs.size()); 888 889 for (EncryptionMethod m: userInfoJWEEncs) 890 stringList.add(m.getName()); 891 892 o.put("userinfo_encryption_enc_values_supported", stringList); 893 } 894 895 if (displays != null) { 896 897 stringList = new ArrayList<>(displays.size()); 898 899 for (Display d: displays) 900 stringList.add(d.toString()); 901 902 o.put("display_values_supported", stringList); 903 } 904 905 if (claimTypes != null) { 906 907 stringList = new ArrayList<>(claimTypes.size()); 908 909 for (ClaimType ct: claimTypes) 910 stringList.add(ct.toString()); 911 912 o.put("claim_types_supported", stringList); 913 } 914 915 if (claims != null) 916 o.put("claims_supported", claims); 917 918 if (claimsLocales != null) { 919 920 stringList = new ArrayList<>(claimsLocales.size()); 921 922 for (LangTag l: claimsLocales) 923 stringList.add(l.toString()); 924 925 o.put("claims_locales_supported", stringList); 926 } 927 928 o.put("claims_parameter_supported", claimsParamSupported); 929 930 // optional front and back-channel logout 931 o.put("frontchannel_logout_supported", frontChannelLogoutSupported); 932 933 if (frontChannelLogoutSupported) { 934 o.put("frontchannel_logout_session_supported", frontChannelLogoutSessionSupported); 935 } 936 937 o.put("backchannel_logout_supported", backChannelLogoutSupported); 938 939 if (backChannelLogoutSupported) { 940 o.put("backchannel_logout_session_supported", backChannelLogoutSessionSupported); 941 } 942 943 return o; 944 } 945 946 947 /** 948 * Parses an OpenID Provider metadata from the specified JSON object. 949 * 950 * @param jsonObject The JSON object to parse. Must not be 951 * {@code null}. 952 * 953 * @return The OpenID Provider metadata. 954 * 955 * @throws ParseException If the JSON object couldn't be parsed to an 956 * OpenID Provider metadata. 957 */ 958 public static OIDCProviderMetadata parse(final JSONObject jsonObject) 959 throws ParseException { 960 961 AuthorizationServerMetadata as = AuthorizationServerMetadata.parse(jsonObject); 962 963 List<SubjectType> subjectTypes = new ArrayList<>(); 964 for (String v: JSONObjectUtils.getStringArray(jsonObject, "subject_types_supported")) { 965 subjectTypes.add(SubjectType.parse(v)); 966 } 967 968 OIDCProviderMetadata op = new OIDCProviderMetadata( 969 as.getIssuer(), 970 Collections.unmodifiableList(subjectTypes), 971 as.getJWKSetURI()); 972 973 // Endpoints 974 op.setAuthorizationEndpointURI(as.getAuthorizationEndpointURI()); 975 op.setTokenEndpointURI(as.getTokenEndpointURI()); 976 op.setRegistrationEndpointURI(as.getRegistrationEndpointURI()); 977 op.setIntrospectionEndpointURI(as.getIntrospectionEndpointURI()); 978 op.setRevocationEndpointURI(as.getRevocationEndpointURI()); 979 980 if (jsonObject.get("userinfo_endpoint") != null) 981 op.userInfoEndpoint = JSONObjectUtils.getURI(jsonObject, "userinfo_endpoint"); 982 983 if (jsonObject.get("check_session_iframe") != null) 984 op.checkSessionIframe = JSONObjectUtils.getURI(jsonObject, "check_session_iframe"); 985 986 if (jsonObject.get("end_session_endpoint") != null) 987 op.endSessionEndpoint = JSONObjectUtils.getURI(jsonObject, "end_session_endpoint"); 988 989 // Capabilities 990 op.setScopes(as.getScopes()); 991 op.setResponseTypes(as.getResponseTypes()); 992 op.setResponseModes(as.getResponseModes()); 993 op.setGrantTypes(as.getGrantTypes()); 994 995 op.setTokenEndpointAuthMethods(as.getTokenEndpointAuthMethods()); 996 op.setTokenEndpointJWSAlgs(as.getTokenEndpointJWSAlgs()); 997 998 op.setIntrospectionEndpointAuthMethods(as.getIntrospectionEndpointAuthMethods()); 999 op.setIntrospectionEndpointJWSAlgs(as.getIntrospectionEndpointJWSAlgs()); 1000 1001 op.setRevocationEndpointAuthMethods(as.getRevocationEndpointAuthMethods()); 1002 op.setRevocationEndpointJWSAlgs(as.getRevocationEndpointJWSAlgs()); 1003 1004 op.setRequestObjectJWSAlgs(as.getRequestObjectJWSAlgs()); 1005 op.setRequestObjectJWEAlgs(as.getRequestObjectJWEAlgs()); 1006 op.setRequestObjectJWEEncs(as.getRequestObjectJWEEncs()); 1007 1008 op.setSupportsRequestParam(as.supportsRequestParam()); 1009 op.setSupportsRequestURIParam(as.supportsRequestURIParam()); 1010 op.setRequiresRequestURIRegistration(as.requiresRequestURIRegistration()); 1011 1012 op.setCodeChallengeMethods(as.getCodeChallengeMethods()); 1013 1014 if (jsonObject.get("acr_values_supported") != null) { 1015 1016 op.acrValues = new ArrayList<>(); 1017 1018 for (String v: JSONObjectUtils.getStringArray(jsonObject, "acr_values_supported")) { 1019 1020 if (v != null) 1021 op.acrValues.add(new ACR(v)); 1022 } 1023 } 1024 1025 // ID token 1026 1027 if (jsonObject.get("id_token_signing_alg_values_supported") != null) { 1028 1029 op.idTokenJWSAlgs = new ArrayList<>(); 1030 1031 for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_signing_alg_values_supported")) { 1032 1033 if (v != null) 1034 op.idTokenJWSAlgs.add(JWSAlgorithm.parse(v)); 1035 } 1036 } 1037 1038 1039 if (jsonObject.get("id_token_encryption_alg_values_supported") != null) { 1040 1041 op.idTokenJWEAlgs = new ArrayList<>(); 1042 1043 for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_encryption_alg_values_supported")) { 1044 1045 if (v != null) 1046 op.idTokenJWEAlgs.add(JWEAlgorithm.parse(v)); 1047 } 1048 } 1049 1050 1051 if (jsonObject.get("id_token_encryption_enc_values_supported") != null) { 1052 1053 op.idTokenJWEEncs = new ArrayList<>(); 1054 1055 for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_encryption_enc_values_supported")) { 1056 1057 if (v != null) 1058 op.idTokenJWEEncs.add(EncryptionMethod.parse(v)); 1059 } 1060 } 1061 1062 // UserInfo 1063 1064 if (jsonObject.get("userinfo_signing_alg_values_supported") != null) { 1065 1066 op.userInfoJWSAlgs = new ArrayList<>(); 1067 1068 for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_signing_alg_values_supported")) { 1069 1070 if (v != null) 1071 op.userInfoJWSAlgs.add(JWSAlgorithm.parse(v)); 1072 } 1073 } 1074 1075 1076 if (jsonObject.get("userinfo_encryption_alg_values_supported") != null) { 1077 1078 op.userInfoJWEAlgs = new ArrayList<>(); 1079 1080 for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_encryption_alg_values_supported")) { 1081 1082 if (v != null) 1083 op.userInfoJWEAlgs.add(JWEAlgorithm.parse(v)); 1084 } 1085 } 1086 1087 1088 if (jsonObject.get("userinfo_encryption_enc_values_supported") != null) { 1089 1090 op.userInfoJWEEncs = new ArrayList<>(); 1091 1092 for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_encryption_enc_values_supported")) { 1093 1094 if (v != null) 1095 op.userInfoJWEEncs.add(EncryptionMethod.parse(v)); 1096 } 1097 } 1098 1099 1100 // Misc 1101 1102 if (jsonObject.get("display_values_supported") != null) { 1103 1104 op.displays = new ArrayList<>(); 1105 1106 for (String v: JSONObjectUtils.getStringArray(jsonObject, "display_values_supported")) { 1107 1108 if (v != null) 1109 op.displays.add(Display.parse(v)); 1110 } 1111 } 1112 1113 if (jsonObject.get("claim_types_supported") != null) { 1114 1115 op.claimTypes = new ArrayList<>(); 1116 1117 for (String v: JSONObjectUtils.getStringArray(jsonObject, "claim_types_supported")) { 1118 1119 if (v != null) 1120 op.claimTypes.add(ClaimType.parse(v)); 1121 } 1122 } 1123 1124 1125 if (jsonObject.get("claims_supported") != null) { 1126 1127 op.claims = new ArrayList<>(); 1128 1129 for (String v: JSONObjectUtils.getStringArray(jsonObject, "claims_supported")) { 1130 1131 if (v != null) 1132 op.claims.add(v); 1133 } 1134 } 1135 1136 if (jsonObject.get("claims_locales_supported") != null) { 1137 1138 op.claimsLocales = new ArrayList<>(); 1139 1140 for (String v : JSONObjectUtils.getStringArray(jsonObject, "claims_locales_supported")) { 1141 1142 if (v != null) { 1143 1144 try { 1145 op.claimsLocales.add(LangTag.parse(v)); 1146 1147 } catch (LangTagException e) { 1148 1149 throw new ParseException("Invalid claims_locales_supported field: " + e.getMessage(), e); 1150 } 1151 } 1152 } 1153 } 1154 1155 op.setUILocales(as.getUILocales()); 1156 op.setServiceDocsURI(as.getServiceDocsURI()); 1157 op.setPolicyURI(as.getPolicyURI()); 1158 op.setTermsOfServiceURI(as.getTermsOfServiceURI()); 1159 1160 if (jsonObject.get("claims_parameter_supported") != null) 1161 op.claimsParamSupported = JSONObjectUtils.getBoolean(jsonObject, "claims_parameter_supported"); 1162 1163 // Optional front and back-channel logout 1164 if (jsonObject.get("frontchannel_logout_supported") != null) 1165 op.frontChannelLogoutSupported = JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_supported"); 1166 1167 if (op.frontChannelLogoutSupported && jsonObject.get("frontchannel_logout_session_supported") != null) 1168 op.frontChannelLogoutSessionSupported = JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_session_supported"); 1169 1170 if (jsonObject.get("backchannel_logout_supported") != null) 1171 op.backChannelLogoutSupported = JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_supported"); 1172 1173 if (op.frontChannelLogoutSupported && jsonObject.get("backchannel_logout_session_supported") != null) 1174 op.backChannelLogoutSessionSupported = JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_session_supported"); 1175 1176 op.setSupportsTLSClientCertificateBoundAccessTokens(as.supportsTLSClientCertificateBoundAccessTokens()); 1177 1178 // Parse custom (not registered) parameters 1179 for (Map.Entry<String,?> entry: as.getCustomParameters().entrySet()) { 1180 if (REGISTERED_PARAMETER_NAMES.contains(entry.getKey())) 1181 continue; // skip 1182 op.setCustomParameter(entry.getKey(), entry.getValue()); 1183 } 1184 1185 return op; 1186 } 1187 1188 1189 /** 1190 * Parses an OpenID Provider metadata from the specified JSON object 1191 * string. 1192 * 1193 * @param s The JSON object sting to parse. Must not be {@code null}. 1194 * 1195 * @return The OpenID Provider metadata. 1196 * 1197 * @throws ParseException If the JSON object string couldn't be parsed 1198 * to an OpenID Provider metadata. 1199 */ 1200 public static OIDCProviderMetadata parse(final String s) 1201 throws ParseException { 1202 1203 return parse(JSONObjectUtils.parse(s)); 1204 } 1205 1206 1207 /** 1208 * Resolves OpenID Provider metadata from the specified issuer 1209 * identifier. The metadata is downloaded by HTTP GET from 1210 * {@code [issuer-url]/.well-known/openid-configuration}. 1211 * 1212 * @param issuer The OpenID Provider issuer identifier. Must represent 1213 * a valid HTTPS or HTTP URL. Must not be {@code null}. 1214 * 1215 * @return The OpenID Provider metadata. 1216 * 1217 * @throws GeneralException If the issuer identifier or the downloaded 1218 * metadata are invalid. 1219 * @throws IOException On a HTTP exception. 1220 */ 1221 public static OIDCProviderMetadata resolve(final Issuer issuer) 1222 throws GeneralException, IOException { 1223 1224 return resolve(issuer, 0, 0); 1225 } 1226 1227 1228 /** 1229 * Resolves OpenID Provider metadata from the specified issuer 1230 * identifier. The metadata is downloaded by HTTP GET from 1231 * {@code [issuer-url]/.well-known/openid-configuration}, using the 1232 * specified HTTP timeouts. 1233 * 1234 * @param issuer The issuer identifier. Must represent a valid 1235 * HTTPS or HTTP URL. Must not be {@code null}. 1236 * @param connectTimeout The HTTP connect timeout, in milliseconds. 1237 * Zero implies no timeout. Must not be negative. 1238 * @param readTimeout The HTTP response read timeout, in 1239 * milliseconds. Zero implies no timeout. Must 1240 * not be negative. 1241 * 1242 * @return The OpenID Provider metadata. 1243 * 1244 * @throws GeneralException If the issuer identifier or the downloaded 1245 * metadata are invalid. 1246 * @throws IOException On a HTTP exception. 1247 */ 1248 public static OIDCProviderMetadata resolve(final Issuer issuer, 1249 final int connectTimeout, 1250 final int readTimeout) 1251 throws GeneralException, IOException { 1252 1253 URL configURL; 1254 1255 try { 1256 URL issuerURL = new URL(issuer.getValue()); 1257 1258 // Validate but don't insist on HTTPS, see 1259 // http://openid.net/specs/openid-connect-core-1_0.html#Terminology 1260 if (issuerURL.getQuery() != null && ! issuerURL.getQuery().trim().isEmpty()) { 1261 throw new GeneralException("The issuer identifier must not contain a query component"); 1262 } 1263 1264 if (issuerURL.getPath() != null && issuerURL.getPath().endsWith("/")) { 1265 configURL = new URL(issuerURL + ".well-known/openid-configuration"); 1266 } else { 1267 configURL = new URL(issuerURL + "/.well-known/openid-configuration"); 1268 } 1269 1270 } catch (MalformedURLException e) { 1271 throw new GeneralException("The issuer identifier doesn't represent a valid URL: " + e.getMessage(), e); 1272 } 1273 1274 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.GET, configURL); 1275 httpRequest.setConnectTimeout(connectTimeout); 1276 httpRequest.setReadTimeout(readTimeout); 1277 1278 HTTPResponse httpResponse = httpRequest.send(); 1279 1280 if (httpResponse.getStatusCode() != 200) { 1281 throw new IOException("Couldn't download OpenID Provider metadata from " + configURL + 1282 ": Status code " + httpResponse.getStatusCode()); 1283 } 1284 1285 JSONObject jsonObject = httpResponse.getContentAsJSONObject(); 1286 1287 OIDCProviderMetadata op = OIDCProviderMetadata.parse(jsonObject); 1288 1289 if (! issuer.equals(op.getIssuer())) { 1290 throw new GeneralException("The returned issuer doesn't match the expected: " + op.getIssuer()); 1291 } 1292 1293 return op; 1294 } 1295}