given the following setup:
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
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.
Run Keycloak v18.0.2
Create development
realm
Create foo
client
Create foo-admin
role
Create some:scope
client scope
Create user
user
Assign foo-admin
role into user
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
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
access -token
's rolejwtd() {
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
You can verify user token by JWT.io web site.
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
curl -s --location 'http://localhost:8080/auth/admin/realms/development/users/'"$USER_ID"'/role-mappings' \
--header 'Authorization: Bearer '"$MASTER_TOKEN" | jq
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
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
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
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
The user
assigned foo-admin
role.
And it's role assigned some:scope
scope.
So user can do under by some:scope
scope.