Token Exchange - Android + Google + Keycloak

1.3k Views Asked by At

Context:

I'm developing a native Android App, authenticating it with Google SignIn and it is working.

I also have a Keycloak server integrate with Google as an identity provider and it also working.

I'm able to import my account from Google on my first login, using the web browser accessing http://localhost:8080/auth/realms/chapa/account/.

When a I go to Keycloak console, I find my account created and linked with Google.

My Google Identity Provider setup was done using (OpenId Connect v1 + https://accounts.google.com/.well-known/openid-configuration) or Social Google on Keycloak.

Both of the ways are working using browser navigation, as I saw few people complaing that Keycloak plugin was broken.

My issue:

On my Android App I can't perform a Token Exchange with Keycloak server.

I did a research and that is the only way to integrate Android + Google + Keycloak as I don't wanna ask my user credentials again. Please, let me know if you know other ways.

On my Keycloak server I get anything else than this WARN:

08:09:48,831 WARN [org.keycloak.events] (default task-11) type=TOKEN_EXCHANGE_ERROR, realmId=my-realm, clientId=android-app, userId=null, ipAddress=172.17.0.1, error=invalid_token, reason='user info call failure', auth_method=token_exchange, grant_type=urn:ietf:params:oauth:grant-type:token-exchange, subject_issuer=https://accounts.google.com, validation_method='user info', client_auth_method=client-secret

That is the request I'm performing against Keycloak, manually on Postman, expecting to exchange the tokens:

curl --location --request POST 'http://localhost:8080/auth/realms/my-realm/protocol/openid-connect/token' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'client_id=android-app' \
    --data-urlencode 'client_secret=a1739b19-3131-4f5c-ba31-8d24afff8d84' \
    --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
    --data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
    --data-urlencode 'subject_token=eyJhbGciOiJSUzI1NiIsImtpZCI6... (truncated)' \
    --data-urlencode 'subject_token_type=urn:ietf:params:oauth:token-type:jwt' \
    --data-urlencode 'subject_issuer=google'

Postman response (400 Bad Request):

{
    "error": "invalid_token",
    "error_description": "invalid token"
}

Android code:

GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestEmail()
    .requestIdToken(clientId) //same client id used on Keycloak Identity Provider
    .requestScopes(new Scope(Scopes.PROFILE), new Scope(Scopes.PLUS_ME), new Scope(Scopes.EMAIL))
    .build();

GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(getApplicationContext(), gso);

GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(getApplicationContext());

Log.w("getServerAuthCode", account.getServerAuthCode()); //null
Log.w("getIdToken", account.getIdToken()); //value passed on Postman subject_token parameter
1

There are 1 best solutions below

0
On

I fixed it changing the way of getting the token:

new AsyncTask<Void, Void, Void>() {
    @Override
    protected Void doInBackground(Void... params) {
        try {
            String accessToken = GoogleAuthUtil.getToken(
                getApplicationContext(),
                account.getAccount().name, "oauth2:"
                        + "https://www.googleapis.com/auth/userinfo.profile" +
                        " https://www.googleapis.com/auth/plus.profile.emails.read");
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }
}.execute();