Search code examples
oauthkeycloak

Keycloak - Assign a client scope to user with a specific role


given the following setup:

  • A user with a realm role "foo-admin"
  • A client named "foo" (Direct Access Grants Enabled, public)
  • A client scope "some:scope" (Optional Client Scope of client "foo")

When requesting an access token with the client "foo" the user should get the scope "some:scope" based on his realm role "foo-admin". Users without that role should not be able to get that scope (even when requesting it).

I followed this blog post and assigned the "foo-admin" admin role under the scope tab of the client scope. As far as i can tell this setup does exactly what we want, but to be honest i find the tooltip in the scope tab confusing, because we did not create a user role mapping or something in the first place. Tooltip: "Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client."

Is this setup ok? How and why does assigning the role in the scope tab work, when the tooltip says something about restricting mappings?

Thanks in advance


Solution

  • User's access token only includes realm roles not it is scope.

    If you want to user's mapping scope, have to call extra REST API calls.

    Overview

    enter image description here

    Setups

    1. Run Keycloak v18.0.2

    2. Create development realm

    3. Create foo client

    4. Create foo-admin role

    5. Create some:scope client scope

    6. Assign foo-admin into some:scope enter image description here

    7. Assign some:scope Optional Client Scope into foo client enter image description here

    8. Create user user

    9. Assign foo-admin role into user

    List item

    Now ready to demo.

    Demo by curl command It will shows, user's access token includes assigned realm role.

    Get Master Token - credential is admin/admin and one hour - for testing enter image description here

    Demo

    1. Get Mater & User access -token
    MASTER_TOKEN=$(curl --location --request POST "http://localhost:8080/auth/realms/master/protocol/openid-connect/token" \
    -s \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'client_id=admin-cli' \
    --data-urlencode 'grant_type=password' \
    --data-urlencode 'username=admin' \
    --data-urlencode 'password=admin' | jq -r '.access_token')
    echo $MASTER_TOKEN
    
    
    ACCESS_TOKEN=$(curl --location --request POST "http://localhost:8080/auth/realms/development/protocol/openid-connect/token" \
    -s \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'client_id=foo' \
    --data-urlencode 'grant_type=password' \
    --data-urlencode 'username=user' \
    --data-urlencode 'password=1234' | jq -r '.access_token')
    echo $ACCESS_TOKEN
    

    Result enter image description here

    enter image description here

    1. Display User access -token 's role
    jwtd() {
        if [[ -x $(command -v jq) ]]; then
             jq -R 'split(".") | .[0],.[1] | @base64d | fromjson' <<< "${1}"
             echo "Signature: $(echo "${1}" | awk -F'.' '{print $3}')"
        fi
    }
    
    jwtd $ACCESS_TOKEN
    

    Result enter image description here

    You can verify user token by JWT.io web site.

    enter image description here

    1. Get User's role's scope

    3.1 Get User's Role (*Overview red circle 3) - foo-admin

    Find user 's user id - jq is useful -r option remove "double quate" '.[0]is get first item of array. .id` get only id field.

    USER_ID=$(curl -s --location 'http://localhost:8080/auth/admin/realms/development/users/?username=user' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN" | jq -r '.[0]'.id)
    echo $USER_ID
    

    enter image description here

    curl -s --location 'http://localhost:8080/auth/admin/realms/development/users/'"$USER_ID"'/role-mappings' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN" | jq
    

    enter image description here

    3.2 Get client foo 's scopes some:scope (*Overview red circle 1)

    Get Client ID

    CLIENT_ID=$(curl -s --location 'http://localhost:8080/auth/admin/realms/development/clients/?clientId=foo' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN" | jq -r '.[0]'.id)
    echo $CLIENT_ID
    

    enter image description here

    Get Client foo 's optional scope list

    curl -s --location 'http://localhost:8080/auth/admin/realms/development/clients/'"$CLIENT_ID"'/optional-client-scopes' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN" | jq
    

    enter image description here

    3.3 Get scope some:scope 's assigned role (*Overview red circle 2) - foo-admin

    Get some:scope scope 's id filter by jq matched some:scope name from scope's list array.

    SCOPE_ID=$(curl -s --location 'http://localhost:8080/auth/admin/realms/development/client-scopes' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN" | jq -r -c '.[] | select(.name | contains("some:scope")).id')
    echo $SCOPE_ID
    

    enter image description here

    From scope id get it's assigned role. it will be foo-admin" role.

    curl -s --location 'http://localhost:8080/auth/admin/realms/development/client-scopes/'$SCOPE_ID'/scope-mappings/realm' \
    --header 'Authorization: Bearer '"$MASTER_TOKEN"  | jq
    

    enter image description here

    Final conclusion

    The user assigned foo-admin role. And it's role assigned some:scope scope. So user can do under by some:scope scope.