This is a clarification to the previous write up about Keycloak Authorization Services . The documentation of the response_mode
documents the two values which can be used: decision
and permissions
. In the first Keycloak article , I have wrongly assumed that no response_mode
in the grant_type=urn:ietf:params:oauth:grant-type:uma-ticket
call implies the value of permissions
.
Mmm, that was a wrong assumption.
Now, looking at the documentation and trying it out, the distinction seems pretty obvious. It turns out there are three types of responses for this grant_type
.
Asking for RPT
(requesting party token - an access token with permissions): without response_mode
parameter
1
2
3
4
5
6
curl --silent -X POST \
${ KEYCLOAK_TOKEN_URL } \
-H "Authorization: Bearer ${ access_token } " \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience=customers" \
--data "permission=CustomerB#customer-b" | jq '.'
Copy
where the ${access_token}
is the outcome of:
1
2
3
4
export access_token = ` curl --silent -u customers:${ KEYCLOAK_CLIENT_SECRET } \
-k -d "grant_type=password&username=member@service-team&password= ${ USER_PASSWORD } &scope=email profile" \
-H "Content-Type:application/x-www-form-urlencoded" \
${ KEYCLOAK_TOKEN_URL } | jq '.access_token' -r`
Copy
the response looks like this, the access_token
is the RPT
:
1
2
3
4
5
6
7
8
9
{
"upgraded" : false ,
"access_token" : "eyJhbGciOiJSUzI1NiIsI...n8AC51T1AMwDtoqfCEXrdwcrQ" ,
"expires_in" : 300 ,
"refresh_expires_in" : 1800 ,
"refresh_token" : "eyJhbGciOiJIUz...RG3zFus" ,
"token_type" : "Bearer" ,
"not-before-policy" : 0
}
Copy
Asking for permissions - decision only: response_mode=decision
By adding:
--data "response_mode=decision"
Copy the full call is:
1
2
3
4
5
6
7
curl --silent -X POST \
${ KEYCLOAK_TOKEN_URL } \
-H "Authorization: Bearer ${ access_token } " \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience=customers" \
--data "response_mode=decision" \
--data "permission=CustomerB#customer-b" | jq '.'
Copy
and returns only a decision. If user has access to the resources, the response is:
1
2
3
{
"result" : true
}
Copy
otherwise, the response is:
1
2
3
4
{
"error" : "access_denied" ,
"error_description" : "not_authorized"
}
Copy
Asking for permissions: response_mode=permissions
By specifying
--data "response_mode=permissions"
Copy full call being:
1
2
3
4
5
6
7
curl --silent -X POST \
${ KEYCLOAK_TOKEN_URL } \
-H "Authorization: Bearer ${ access_token } " \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience=customers" \
--data "response_mode=permissions" \
--data "permission=CustomerB#customer-b" | jq '.'
Copy
Keycloak answers:
1
2
3
4
5
6
7
8
9
[
{
"scopes" : [
"customer-b"
],
"rsid" : "00f34b81-c45b-4e28-b267-45fad4e48b4d" ,
"rsname" : "CustomerB"
}
]
Copy
However , what is more interesting, is the call without specific permissions listed:
1
2
3
4
5
6
curl --silent -X POST \
${ KEYCLOAK_TOKEN_URL } \
-H "Authorization: Bearer ${ access_token } " \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience=customers" \
--data "response_mode=permissions" | jq '.'
Copy
which returns all available permissions for the original access token:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"scopes" : [
"customer-a"
],
"rsid" : "715f6cc5-8ca7-44e4-a8ce-924493db76b1" ,
"rsname" : "CustomerA"
},
{
"scopes" : [
"customer-b"
],
"rsid" : "00f34b81-c45b-4e28-b267-45fad4e48b4d" ,
"rsname" : "CustomerB"
}
]
Copy
§ Listing permissions without the name
Optionally, we can ask Keycloak to not return resource names, only IDs. This is achieved by using response_include_resource_name=false
, an example:
1
2
3
4
5
6
7
curl --silent -X POST \
${ KEYCLOAK_TOKEN_URL } \
-H "Authorization: Bearer ${ access_token } " \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience=customers" \
--data "response_mode=permissions" \
--data "response_include_resource_name=false" | jq '.'
Copy
gives:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
{
"scopes" : [
"customer-a"
],
"rsid" : "715f6cc5-8ca7-44e4-a8ce-924493db76b1"
},
{
"scopes" : [
"customer-b"
],
"rsid" : "00f34b81-c45b-4e28-b267-45fad4e48b4d"
}
]
Copy