You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the Issue
When I try to use the BFF pattern with an OIDC authentication server, in my case Keycloak, I encounter a bug:
In the link above at line 8, the issuer is assigned this value: ${reverse-proxy-uri}${authorization-server-prefix}/realms/baeldung
However, per the ClientRegistration class defined in org.springframework.security.oauth2.client.registration, at line 132, there's a comparison between, the issuer provider above and the actual issuer info that comes from the server.
If the server doesn't use the BFF pattern, this wouldn't be an issue, because this comparison will always be true.
However if the authentication server is between a Reverse-Proxy, aka gateway, then this comparison will be false because the BFF pattern modifies the issuer uri as per 1. and although that modified link still point to the right server, the code at line 132 above would make it fails because there's not a one-to-one match between the modified uri in the bff configuration and the one returned from the server
5. Server configuration link: http://localhost:8080/realms/myrealm/.well-known/openid-configuration
`{"issuer":"http://localhost:8080/realms/myrealm","authorization_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/auth","token_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/token","introspection_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect","userinfo_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/userinfo","end_session_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/logout","frontchannel_logout_session_supported":true,"frontchannel_logout_supported":true,"jwks_uri":"http://localhost:8080/realms/myrealm/protocol/openid-connect/certs","check_session_iframe":"http://localhost:8080/realms/myrealm/protocol/openid-connect/login-status-iframe.html","grant_types_supported":["authorization_code","implicit","refresh_token","password","client_credentials","urn:ietf:params:oauth:grant-type:device_code","urn:openid:params:grant-type:ciba"],"acr_values_supported":["0","1"],"response_types_supported":["code","none","id_token","token","id_token token","code id_token","code token","code id_token token"],"subject_types_supported":["public","pairwise"],"id_token_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"id_token_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"id_token_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"userinfo_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"userinfo_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"request_object_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"request_object_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"request_object_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"response_modes_supported":["query","fragment","form_post","query.jwt","fragment.jwt","form_post.jwt","jwt"],"registration_endpoint":"http://localhost:8080/realms/myrealm/clients-registrations/openid-connect","token_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"token_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"introspection_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"introspection_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"authorization_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"claims_supported":["aud","sub","iss","auth_time","name","given_name","family_name","preferred_username","email","acr"],"claim_types_supported":["normal"],"claims_parameter_supported":true,"scopes_supported":["openid","profile","roles","web-origins","phone","microprofile-jwt","acr","email","address","offline_access"],"request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"code_challenge_methods_supported":["plain","S256"],"tls_client_certificate_bound_access_tokens":true,"revocation_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/revoke","revocation_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"revocation_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"device_authorization_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/auth/device","backchannel_token_delivery_modes_supported":["poll","ping"],"backchannel_authentication_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/ext/ciba/auth","backchannel_authentication_request_signing_alg_values_supported":["PS384","ES384","RS384","ES256","RS256","ES512","PS256","PS512","RS512"],"require_pushed_authorization_requests":false,"pushed_authorization_request_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/ext/par/request","mtls_endpoint_aliases":{"token_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/token","revocation_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/revoke","introspection_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect","device_authorization_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/auth/device","registration_endpoint":"http://localhost:8080/realms/myrealm/clients-registrations/openid-connect","userinfo_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/userinfo","pushed_authorization_request_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/ext/par/request","backchannel_authentication_endpoint":"http://localhost:8080/realms/myrealm/protocol/openid-connect/ext/ciba/auth"}}`
7. The issuer value from the BFF configuration is: http://localhost:7080/auth/realms/myrealm
The issuer value as retrieved in line 130 of ClientRegistrations is: http://localhost:8080/realms/myrealm
These two lines are compared at line 131 of ClientRegistrations,
```
Assert.state(issuer.equals(metadataIssuer), () -> {
return "The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did not match the requested issuer \"" + issuer + "\"";
});
Because the two uris aren't equals, an exception is thrown:
Caused by: java.lang.IllegalStateException: The Issuer "http://localhost:8080/realms/myrealm" provided in the configuration metadata did not match the requested issuer "http://localhost:7080/auth/realms/myrealm"
at org.springframework.util.Assert.state(Assert.java:97) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.withProviderConfiguration(ClientRegistrations.java:246) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:165) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:216) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.fromIssuerLocation(ClientRegistrations.java:152) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.getBuilderFromIssuerIfPossible(OAuth2ClientPropertiesMapper.java:97) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.getClientRegistration(OAuth2ClientPropertiesMapper.java:71) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.lambda$asClientRegistrations$0(OAuth2ClientPropertiesMapper.java:65) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at java.base/java.util.HashMap.forEach(HashMap.java:1421) ~[na:na]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.asClientRegistrations(OAuth2ClientPropertiesMapper.java:64) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientRegistrationRepositoryConfiguration.clientRegistrationRepository(OAuth2ClientRegistrationRepositoryConfiguration.java:49) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.8.jar:6.1.8]
**Expected Behavior**
Given that the BFF pattern only masks the link of the authentication server, the two links return the same result, so an exception shouldn't be thrown
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS: [e.g. Windows]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Additional Context**
The solution here would be to change the issuer in the BFF configuration to the actual value of the issuer but this will defeat the purpose of using a Reverse-Proxy and thus is not a recommended solution. If would be best, instead of only comparing the links values, the results return by these links should be compared as well. That way if a Reverse-Proxy is used, the first option, comparing the links, will fail but the second option, comparing the results return by the links, will succeed and thus the two links should be marked equal
The text was updated successfully, but these errors were encountered:
As a reminder, OpenID Providers (OPs) are OAuth2 authorization servers and OpenID Relying Parties (RPs) are OAuth2 clients and resource servers.
The issuer property in the project YAML is intended to contain an OP identifier.
This issuer identifier is used for several things:
define where the OpenID configuration is exposed: ${issuer}/.well-known/openid-configuration. RPs auto-configure endpoints for authorization, token, JWK-set, RP-Initiated Logout, etc. from it
define the value of the issuer property in the OpenID configuration (which must be the exact issuer identifier).
set the tokens iss claim value. The issuer validator in JWT decoders checks for an exact match between tokens iss claim and the issuer identifier (as the OpenID spec recommends).
In the project, the requests to the authorization server don't go through the OAuth2 BFF (the Gateway). The reverse proxy is an Nginx instance standing before both Keycloak and the BFF. This reverse proxy doesn't alter the path when routing requests to Keycloak which is configured to be served with a /auth prefix. So the issuer identifier is composed of the reverse proxy authority (port 7080, not 8080 which is used by the Gateway), Keycloak's relative path (/auth) and realm prefix (/realms/baeldung), as it should be.
If using a different reverse proxy configuration (different server or path segments added/removed), then Keycloak's configuration should be adapted so that the issuer identifier it uses (to build the OpenID configuration and set the iss claim value in tokens) is what RPs use to reach it.
Article and Module Links
https://github.com/eugenp/tutorials/blob/master/spring-security-modules/spring-security-oauth2-bff/backend/bff/src/main/resources/application.yml
Describe the Issue
When I try to use the BFF pattern with an OIDC authentication server, in my case Keycloak, I encounter a bug:
To Reproduce
Steps to reproduce the behavior:
#Keycloak configuration
scheme=http
hostname=localhost
reverse-proxy-port=7080
reverse-proxy-uri=${scheme}://${hostname}:${reverse-proxy-port}
authorization-server-prefix=/auth
issuer=${reverse-proxy-uri}${authorization-server-prefix}/realms/myrealm
client-id=myrealm-bff
client-secret=TrUt37XU00u2n0n30oW4isLiju2uG0wG
username-claim-json-path=$.preferred_username
authorities-json-path=$.realm_access.roles
bff-port=7081
bff-prefix=/bff
bff-uri=${scheme}://${hostname}:${bff-port}
resource-server-port=8083
audience=
Caused by: java.lang.IllegalStateException: The Issuer "http://localhost:8080/realms/myrealm" provided in the configuration metadata did not match the requested issuer "http://localhost:7080/auth/realms/myrealm"
at org.springframework.util.Assert.state(Assert.java:97) ~[spring-core-6.1.8.jar:6.1.8]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.withProviderConfiguration(ClientRegistrations.java:246) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:165) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:216) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.security.oauth2.client.registration.ClientRegistrations.fromIssuerLocation(ClientRegistrations.java:152) ~[spring-security-oauth2-client-6.3.0.jar:6.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.getBuilderFromIssuerIfPossible(OAuth2ClientPropertiesMapper.java:97) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.getClientRegistration(OAuth2ClientPropertiesMapper.java:71) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.lambda$asClientRegistrations$0(OAuth2ClientPropertiesMapper.java:65) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at java.base/java.util.HashMap.forEach(HashMap.java:1421) ~[na:na]
at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper.asClientRegistrations(OAuth2ClientPropertiesMapper.java:64) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientRegistrationRepositoryConfiguration.clientRegistrationRepository(OAuth2ClientRegistrationRepositoryConfiguration.java:49) ~[spring-boot-autoconfigure-3.3.0.jar:3.3.0]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.8.jar:6.1.8]
The text was updated successfully, but these errors were encountered: