+
Skip to content

Can't impersonate a user cross-realm? #38184

Closed as not planned
Closed as not planned
@gatos-cc

Description

@gatos-cc

Before reporting an issue

  • I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

Area

token-exchange

Describe the bug

I'm following this guide to enable token exchange between two users in Keycloak. I've also read through the official docs a few times now.

What I'm trying to do is have a back end service log in as user A in realm A, and then exchange that token for user B in realm B. Both users already exist.

Everything seems to work at first - This gets me an access token for user A in realm A:

KEYCLOAK_HOST="https://..."
SOURCE_REALM_NAME="TEST-source"
SOURCE_CLIENT_NAME="realm_A_login"
SOURCE_CLIENT_SECRET="..."
SOURCE_REALM_USER_NAME="user_A"
SOURCE_REALM_USER_PASSWORD="..."
SOURCE_TOKEN=$(curl -s \
    -X POST \
    "${KEYCLOAK_HOST}/realms/${SOURCE_REALM_NAME}/protocol/openid-connect/token" \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d "client_id=${SOURCE_CLIENT_NAME}" \
    --data-urlencode "username=${SOURCE_REALM_USER_NAME}" \
    --data-urlencode "password=${SOURCE_REALM_USER_PASSWORD}" \
    -d 'grant_type=password' \
    -d "client_secret=${SOURCE_CLIENT_SECRET}" \
    -d 'scope=openid profile roles' \
    | jq -r .access_token)
echo "SOURCE_TOKEN: ${SOURCE_TOKEN}"

Pulling apart that JWT, I get what I would expect:

{
  "exp": 1742287643,
  "iat": 1742244443,
  "aud": "account",
  "sub": "98969f6f-38b7-4bd1-b353-957b88721919",
  "typ": "Bearer",
  ...
  "scope": "openid email profile",
  "email_verified": true,
  "name": "User A",
  "preferred_username": "user_A",
  ...
}

Now, exchange that for a token for user B:

DESTINATION_REALM_NAME="TEST-Destination"
DESTINATION_CLIENT="realm_B_login"
DESTINATION_CLIENT_SECRET="..."
DESTINATION_IDP_NAME="IdP_TEST-source"
USERNAME="........-....-....-....-............"
ACCESS_TOKEN=$(curl -L 
  "${KEYCLOAK_HOST}/realms/${DESTINATION_REALM_NAME}/protocol/openid-connect/token" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
  -d "subject_token=${SOURCE_TOKEN}" \
  --data-urlencode 'subject_token_type=urn:ietf:params:oauth:token-type:access_token' \
  -d "client_id=${DESTINATION_CLIENT}" \
  -d "client_secret=${DESTINATION_CLIENT_SECRET}" \
  -d "subject_issuer=${DESTINATION_IDP_NAME}" \
  -d "audience=${DESTINATION_CLIENT}" \
  -d "requested_subject=${USERNAME}" \
  -d 'scope=openid profile roles' | jq -r .access_token)
echo "ACCESS_TOKEN: ${ACCESS_TOKEN}"

That seems great - I get an access token back. However, inspecting the token, I seem to have the same user A details:

{
  "exp": 1742287643,
  "iat": 1742244443,
  "jti": "5cf7a005-2c2f-464c-868e-3219c344b543",
  "aud": [
    "account",
    "realm_B_login"
  ],
  "sub": "a91794b9-f566-4162-abf7-544e3127ab70",
  "typ": "Bearer",
  "azp": "realm_B_login",
  "sid": "c7ffe470-0024-42d0-bbad-792ee092d2d3",
  ...
  "scope": "openid email profile",
  "email_verified": false,
  "name": "User A",
  "preferred_username": "user_a@my.company",
  ...
}

So - the audience is a little different, and its using email for a username, but the big issue is its for the user A, not user B.

Pulling up the Keycloak console - lo and behold it created a copy of user A in realm B - and that's what its giving me a token for.

What am I missing here? I'm not asking it to create a new user in my destination realm, and I'm clearly saying my requested_subject is user B. Why is it creating a copy of user A, and why isn't it giving me an access token for user B?

Version

Keycloak 25.0.1

Regression

  • The issue is a regression

Expected behavior

I would expect the token-exchange to mint me an access token for the existing user B, and not create a new user A in the destination realm.

Actual behavior

User A gets duplicated into realm B, and I get an access token minted for that new shadow user.

How to Reproduce?

on server - enable fine grained permissions and token exchange / feature preview

create realm A
create realm B

create user A in realm A, set password

with realm A:

  • create service login client, providing direct access
  • create token_exchange client (will be used for IdP sso exchange)

with realm B:

  • create realm B login client
  • create realm A IdP
    • use well-known/configuration endpoint from realm A
    • specify client/secret for token_exchange from realm A
    • enable fine-grained permissions
  • create a new token-exchange client policy, add the IdP token-exchange permission to the policy
  • create user B

Run the bash snippets above to obtain token for user A in realm A, and then exchange for token for user B in realm B.

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载