Hi Gary,
I'm still convinced this could be done much simpler. Just performed a quick test:
- created a realm;
- created an OIDC client of type "confidential";
- enabled service account;
- went to Service Account Roles and assigned realm-management.manage-realm role;
Then ran a script:
#!/bin/sh
KEYCLOAK_URL=http://localhost:8080/auth
KEYCLOAK_REALM=test-realm
KEYCLOAK_CLIENT=test-client
KEYCLOAK_CLIENT_SECRET=...
ACCESS_TOKEN=$(curl -s $KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/token
\
--basic --user $KEYCLOAK_CLIENT:$KEYCLOAK_CLIENT_SECRET \
-d grant_type=client_credentials \
| jq -r '.access_token')
echo $ACCESS_TOKEN | sed 's/\(.*\)\.\(.*\)\.\(.*\)/\2/' | base64 -d | jq
I've been able to see the following inside the token:
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"realm-management": {
"roles": [
"manage-realm" // <- this
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
(How it works: by default, built-in client scopes are assigned to the clients (see Client
Scopes tab -> Assigned Default Client Scopes).
One of them, namely "roles", invokes the "client roles" mapper, which
in its turn resolves the roles and puts them into a token.)
Could you please try to reproduce the same on your instance? Hope that helps,
Dmitry Telegin
Opensource IAM consultant
https://www.linkedin.com/in/d-telegin
On Tue, 2019-05-07 at 03:40 +0000, Gary Kennedy wrote:
I think I've found an acceptable solution, can I get some
feedback please? I don't want to risk any loopholes or lessened security here.
Originally I was thinking that I needed to add the "resource_access" claim to
the token (with "realm-management" roles), because the admin API was using the
"KeycloakIdentity" class in "MgmtPermissions.hasOneAdminRole" calls.
However, now I've gone with changing the "azp" (issued-for) claim instead,
so that "MgmtPermissions.initIdentity" uses the "UserModelIdentity"
class instead. FYI - I've arbitrarily chosen to use the "admin-cli" client
id for the "azp" claim, but "security-admin-console" would work just
as well.
This is all accomplished with a single client specific script mapper with no "Token
Claim Name", no "Claim JSON Type", and set to only "Add to access
token" (ie, ID token and user info flags are OFF).
The script:
```
// todo: can we make the service account detection a bit more robust? ie, sub claim??
// note: could also check client session notes for form data scope so we don't always
set the issued-for
var tokenName = null;
if (token !== null && token.getOtherClaims() !== null) {
tokenName = token.getOtherClaims().get('preferred_username');
}
if (tokenName == 'service-account-test-client') {
// admin-cli is to get the admin api to use the usermodel instead of the token for
roles
// you could also use the security-admin-console client id
// see MgmtPermissions.initIdentity
token.issuedFor('admin-cli');
}
```
Digging through the code I cannot find any other means of setting the issued-for of the
token during the client credentials grant, but am I missing something? Is there another
way we can set the issued-for at token request time?
Cheers,
Gary
> > > On 7 May 2019, at 9:38 am, Gary Kennedy <gary(a)apnic.net> wrote:
>
> > > > > On 3 May 2019, at 8:38 am, Dmitry Telegin
<demetrio(a)carretti.pro> wrote:
> >
> > Hi Gary,
> >
> > To ensure proper "resource_access" claim, you can simply assign the
necessary roles to your service account (client -> Service Account Roles -> Client
Roles -> realm-management). Does that work for you?
>
> Unfortunately no.
>
> The roles are set, however they are not presented in the token, eg no
"resource_access" claim.
>
> And because of the missing "resource_access" claim, using the token with
the admin API results in 403 forbidden.
>
> > If you still need to use mappers, there are numerous ways to determine if the
token was issued for a service account. For example, in your JS mapper you could look for
"preferred_username" claim, its value will look like
"service-account-<your-client>".
>
> Thanks. I previously explicitly tried the built-in "client roles" mapper
for the client as well as creating a "user client role" mapper manually (not at
the same time) and they were not adding the claim to the token so I assumed wrongly that
the client mappers were not being used for the service account token.
>
> Using a script mapper (and a hardcoded claim mapper) works in that the service
account token has the configured claims from those mappers. It seems like the "user
client roles" mapper type is being filtered from the applied protocol mappers here.
>
> The mapper is applied to user tokens as well (of course) but at least using a script
mapper will allow me to hack in the "resource_access" claim as I want. I'd
like to do the right thing and have the script mapper use actual roles but I may have to
fall back to hardcoding the claim value, we'll see how much effort is needed and that
I'm allowed to put in :p.
>
> > Cheers,
> > Dmitry
> >
> > On Thu, 2019-05-02 at 06:18 +0000, Gary Kennedy wrote:
> > > I want to use a service account token to call the admin API (for it's
realm) and have discovered that the token needs the "resource_access" claim
(with appropriate "realm-management" roles).
> > >
> > > I don't want user tokens generated through the client to have the
claim (unless absolutely necessary).
> > >
> > > How can I get mappers to only apply to the service account token? Or find
the mappers used for the service account tokens?
> > >
> > > If I add the client roles mapper to the client I still don't get the
"resource_access" claim in the service account token.
> > >
> > > (Keycloak 4.8.2)
> > >
> > > Cheers,
> > > Gary
> > >
> > > _______________________________________________
> > > keycloak-user mailing list
> > > keycloak-user(a)lists.jboss.org
> > >
https://lists.jboss.org/mailman/listinfo/keycloak-user
>
> _______________________________________________
> keycloak-user mailing list
> keycloak-user(a)lists.jboss.org
>
https://lists.jboss.org/mailman/listinfo/keycloak-user