Description
Before reporting an issue
- I have searched existing issues
- I have reproduced the issue with the latest release
Area
token-exchange
Describe the bug
Following error is seen in keycloak logs when an external to internal token-exchange request is made to exchange Google ID Token with Keycloak Access Token
2023-04-30 12:53:04,984 WARN [org.keycloak.events] (executor-thread-22) type=TOKEN_EXCHANGE_ERROR, realmId=9edbb8fd-a6cc-4cf3-9632-4293bdc76f3f, clientId=myclient, userId=null, ipAddress=172.17.0.1, error=invalid_token_type, reason='subject_token_type invalid', auth_method=token_exchange, grant_type=urn:ietf:params:oauth:grant-type:token-exchange, subject_issuer=google, client_auth_method=client-secret
Version
21.1.0
Expected behavior
As per the documentation here https://www.keycloak.org/docs/latest/securing_apps/#external-token-to-internal-token-exchange -
- a new user is imported into your realm if it doesn’t exist
- When the exchange is complete, a user session will be created within the realm, and you will receive an access and or refresh token depending on the requested_token_type parameter value
Based on the above, expectation is that Keycloak should create an user based on the user information in Google ID token and should return an access token
Actual behavior
Keycloak returning "HTTP 400 Bad Request" with this error {"error":"invalid_token","error_description":"invalid token type"}
Error in KeyCloak logs
2023-04-30 12:53:04,984 WARN [org.keycloak.events] (executor-thread-22) type=TOKEN_EXCHANGE_ERROR, realmId=9edbb8fd-a6cc-4cf3-9632-4293bdc76f3f, clientId=myclient, userId=null, ipAddress=172.17.0.1, error=invalid_token_type, reason='subject_token_type invalid', auth_method=token_exchange, grant_type=urn:ietf:params:oauth:grant-type:token-exchange, subject_issuer=google, client_auth_method=client-secret
How to Reproduce?
- Start Keycloak with
--features=token-exchange,admin-fine-grained-authz
enabled - Configure Google as Identity Provider in Keycloak
- Configure Keycloak as described here https://www.keycloak.org/docs/latest/securing_apps/#external-token-to-internal-token-exchange
- Get a Google ID Token (outside keycloak)
- Post a request to keycloak to exchange this ID token with a Keycloak internal access token
export id_token=eyJhbGciOiJSUzI1N...
curl -v -X POST http://localhost:8180/realms/habits/protocol/openid-connect/token \
--data-urlencode "client_id=myclient" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "subject_token=$id_token" \
--data-urlencode "subject_issuer=google" \
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:id_token"
Anything else?
Could this be the probable cause?
@Override
protected BrokeredIdentityContext exchangeExternalImpl(EventBuilder event, MultivaluedMap<String, String> params) {
return exchangeExternalUserInfoValidationOnly(event, params);
}
The linked function returns exchangeExternalUserInfoValidationOnly()
which is defined in AbstractOAuth2IdentityProvider.java
and expects only ACCESS_TOKEN
Fix: The linked function in GoogleIdentityProvider.java should return exchangeExternalImpl()
from the base class OIDCIdentityProvider
as exchangeExternalImpl()
validates id_token and JWT as well.