Describe the bug

The section 4.3 in the OpenID Connect Discovery specification says "the Issuer URL that was used as the prefix to /.well-known/openid-configuration". However, the validation in ClientRegistrations.class does an equal() comparison of the entire issuer URL which is comparing too much because a provider's Issuer URL might contain additional parameters.

For example, in Azure AD B2C multiple OIDC configurations could be using the same Issuer and configuration URL in Azure AD B2C is not an straightforward URL. An additional parameter p is added at the end to identify which configurations you want to retrieve.

Usual Provider:

https://someprovider.com/someprefix/v2.0/

Azure AD B2C:

https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/?p=b2c_1_policy_name_a
{
  "issuer": "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/",
  "authorization_endpoint": "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/oauth2/v2.0/authorize?p=b2c_1_policy_name_a",
...
}

https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/?p=b2c_1_policy_name_b
{
  "issuer": "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/",
  "authorization_endpoint": "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/oauth2/v2.0/authorize?p=b2c_1_policy_name_b",
...
}

To Reproduce

Follow the procedure at https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-active-directory-b2c-oidc

Because the well known uri is https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/.well-known/openid-configuration?p=b2c_1_policy_name_a, this Issuer URL must be configurated with additional ?p=b2c_1_policy_name_a at the end.

# application.properties
spring.security.oauth2.client.provider.b2c.issuer-uri=https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/?p=b2c_1_policy_name_a

The following exception will be thrown during start up.

Caused by: java.lang.IllegalStateException: The Issuer "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/" provided in the configuration metadata did not match the requested issuer "https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/?p=b2c_1_policy_name_a"
    at org.springframework.util.Assert.state(Assert.java:101) ~[spring-core-6.2.1.jar:6.2.1]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.withProviderConfiguration(ClientRegistrations.java:286) ~[main/:na]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:205) ~[main/:na]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:256) ~[main/:na]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.fromIssuerLocation(ClientRegistrations.java:192) ~[main/:na]

The exception was due to this method is comparing https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/ (metadata.getIssuer().getValue()) and https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/?p=b2c_1_policy_name_a (issuer).

    private static ClientRegistration.Builder withProviderConfiguration(AuthorizationServerMetadata metadata, String issuer) {
        String metadataIssuer = metadata.getIssuer().getValue();
        Assert.state(issuer.equals(metadataIssuer),
                () -> "The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did "
                        + "not match the requested issuer \"" + issuer + "\"");

Expected behavior The validation should be validating whether the issuer value from well known configuration is the prefix of Issuer URL. defined in spring.security.oauth2.client.provider.b2c.issuer-uri= without taking into account of additional query string parameters .

Possible Solution

I've tested in my local that using issuer.startsWith(metadataIssuer) instead of issuer.equals(metadataIssuer) could avoid the issue and it seems more conform to the specification as in "the Issuer URL that was used as the prefix to /.well-known/openid-configuration".

e.g.

        Assert.state(issuer.startsWith(metadataIssuer),
                () -> "The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did "
                        + "not match the requested issuer \"" + issuer + "\"");

Comment From: abchau

sample.zip

Comment From: sjohnr

@abchau, thanks for getting in touch!

The section 4.3 in the OpenID Connect Discovery specification says "the Issuer URL that was used as the prefix to /.well-known/openid-configuration". However, the validation in ClientRegistrations.class does an equal() comparison of the entire issuer URL which is comparing too much because a provider's Issuer URL might contain additional parameters.

The spec states (emphasis added by me):

The issuer value returned MUST be identical to the Issuer URL that was used as the prefix to /.well-known/openid-configuration to retrieve the configuration information.

This makes it quite clear that the requirement is to use strict equality to validate the issuer returned.

Because the well known uri is https://eaxmple.b2clogin.com/00000000-0000-0000-00000000-000000000000/v2.0/.well-known/openid-configuration?p=b2c_1_policy_name_a, this Issuer URL must be configurated with additional ?p=b2c_1_policy_name_a at the end.

Adding additional data to the request, such as ?p=b2c_1_policy_name_a would be off-spec, and require customization on your end. I believe you would want to influence the outbound request separately from specifying the issuer URI.

However, I believe doing so is not in the spirit of the specification and it appears the provider is deviating from that. The well-known URL is very well defined, and should not require additional parameters. If you disagree, please provide a reference in the spec where provision is made for this requirement. Otherwise, I think the next step would be exploring other options for working around this issue.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.