I am wanting to use Keycloak to authorise access for my API.
I have got the relevant scopes defined, and these are coming back in the access token as expected:
{
... claims ...
"scope": "openid profile user/Patient.write user/Patient.read",
... etc ...
}
but the server hosting the APIs expects scopes in an access token represented as an array, like this:
{
... claims ...
"scope": [
"openid",
"profile",
"user/Patient.read",
"user/Patient.write"
],
...etc...
}
I can't see anywhere in Keycloak where I could alter the behaviour to output scopes as an array?
I've looked at doing it using a custom token mapper script, but the scopes don't appear to be available in mapper scripts, so it doesn't look like I can re-map them that way. Is there any way of getting the scopes into this form in the token in Keycloak?
Related
I want a custom scope called avm.read to be inclued in the scopes of the access token when requesting it.
I have an ApiResource called compApi and an ApiResourceScope called avm.read.
I've also added an ApiScope avm.read and added the avm.read to the ClientScopes.
When requesting the token, I'm also requesting the following scopes:
openid
profile
email
avm.read
After retrieving the token and deserializing it with jwt.io, I only see the openid in the scopes array:
"scope": [
"openid"
],
What do I have to do in addition to what I already did so that the avm.read custom scope is also included in the scope array of the token?
Thanks in advance
what I am trying to do:
I have an app that takes in login credentials: username and password for a user. I have a rest api that internally calls the keycloak REST API: /auth/realms/realmname/protocol/openid-connect/token
and gets the access token for this user.
Now I am building another REST API to access a resource where I want to do the following:
doSomething(accesstoken, data)
{
a) call keycloak API to validate access token and get roles.
b) if role == manager, process(data)
c) else: return error msg.
}
Now, how do I do (a): validating the access token and getting the roles associated with it.
I know we can do: auth/realms/realmname/protocol/openid-connect/userinfo
but that only gives the details about the user like name, email, etc. but does not display any roles.
Here's an example I got:
{
"name": "test user",
"sub": "e2bad34d-a1a9-4d70-ac84-bd3a3246023e",
"email_verified": false,
"preferred_username": "user",
"given_name": "test",
"family_name": "user"
}
As seen, it doesnt give the roles at all. How do I then tell what roles this access token has? Interestingly, when I search for this, many resources are suggesting the above userinfo endpoint. But this merely tells me taht the access token I provided is valid. Does not give roles for that.
In other words - it authenticates but does not authorize.
Please suggest.
Thanks,
Anand
In Keycloak admin Console, you can configure Mappers under your client. Add a builtin Mapper of type "User Realm Role", then open its configuration e.g. change Token Claim Name if you want.
Client roles can be configured similarly, but they are returned by default in the token under the name resource_access.${client_id}.roles
The the client side you can parse the token to find the roles. E.g. In an angular application and using the keycloak-angular adapter, you can have a the token as a json object by calling keycloak.getKeycloakInstance().tokenParsed.
In a spring boot application and using the Keycloak java api, you can find the roles under the field "otherClaim" in the following class
https://www.keycloak.org/docs-api/10.0/javadocs/org/keycloak/representations/AccessTokenResponse.html
In both representations you will find the roles under the "Token Claim Name" defined in the client mapper configuration
Additionally, if the full scope is not allowed then you need to add the relevant roles to the scope, so they can appear in the token.
After adding role in the roles section , need to move available roles into the Assigned Roles of the scope tab of the respective client section.
I am using App ID as an Identity Provider and Authorization Server to protect some back-end spring-boot applications.
I have managed to set up the whole OAuth 2.0 Authorization Code flow to work but cannot manage to include custom scopes into the access token.
The only scopes that appear in the access token are the App ID default ones:
"openid appid_default appid_readuserattr appid_readprofile appid_writeuserattr appid_authenticated"
I have configured an appropriate role with the desired custom scopes and associated this role to the user profile. Furthermore I have associated these custom scopes to the client application. Everything seems fine in the App ID dashboard.
However when I call the token endpoint either programmatically or via curl I always get the same default scopes in the access token.
Reading the Swagger , I should be able to specify the scopes for the password flow and bearer token but I am in an OAuth 2.0 Authorization Code flow. Furthermore, even with password credentials flow, I do not manage to get these custom scopes although I specify them in the request.
Has anyone encountered these problems?
Any help would be much appreciated.
Many Thanks,
Chris
In order to see the application configured scopes in the token, you need to authenticate with the application that you configured scopes to and with the user you assigned the role to.
Meaning you should use username : client ID and password : secret of the application in the request authorization header, and authenticate with the user you assigned the matching role (which contains the scopes wanted).
The steps to add access control to your application:
Go to Applications and define the application that you want to protect by adding scopes.
Create your roles by going to Roles and profiles > Roles > Create role.
Assign the roles to specific users by going to Roles and profiles >
User profiles. Then, choose the user that you want to assign the
role to and click the More options menu > Assign role.
For more information see AppID Access control docs: https://cloud.ibm.com/docs/services/appid?topic=appid-access-control
I have an App ID instance in us-south, and scopes are working fine for me with default Cloud Directory.
create a new application (define your scopes)
create a role and associate your application scope
assign the role to a user
call /token endpoint
It happened to me before, I found that one way to solve it would be to inject the roles into the token claim and then instruct Spring Security to extract them. I wrote about it here in detail. The documentation explains the first part, but the gist is this cURL snippet :
curl -X PUT "https://$REGION.appid.cloud.ibm.com/management/v4/$TENANT_ID/config/tokens" -H 'Content-Type: application/json' -H "Authorization: Bearer $IAM_TOKEN" -d '{
"access": {
"expires_in": 3600
},
"refresh": {
"enabled": true,
"expires_in": 2592001
},
"anonymousAccess": {
"enabled": false
},
"accessTokenClaims": [
{
"source": "roles"
}
],
"idTokenClaims": [
{
"source": "saml",
"sourceClaim": "attributes.uid"
}
]
}'
You can also do it in the Swagger UI. Note however that this is a PUT request, so it's going to overwrite any configuration you had beforehand. Ideally, run a GET request to get the current configuration, then add the claims into it to avoid issues.
Then, in the SecurityConfiguration, add this JWT converter :
protected void configure(HttpSecurity http) throws Exception {
http
//...
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
}
Converter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("authorities");
converter.setAuthorityPrefix(""); //so that the role has the same name as the one that comes from App ID
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtAuthenticationConverter;
}
Now that Spring Security recognizes the roles, you can protect endpoints with annotations or with an antMatcher configuration :
.antMatchers("/api/admin").hasRole("ADMIN")
I am having trouble connecting a few dots with OAuth and Active Resource. Here is what I know: The Active Resource documentation tells me that I can set authentication tokens on the ActiveResource model itself like so:
ActiveResource::Base.connection.auth_type = :bearer
ActiveResource::Base.connection.bearer_token = #bearer_token
class Estimate < ActiveResource::Base
self.connection.auth_type = :bearer
self.connection.bearer_token = #bearer_token
self.site = "https://apistaging.uship.com/v2/estimate"
end
Also, in a totally seperate part of my code, I can retrieve the bearer token I need with the following basic HTTP requests (omitting my actual client id and secret for privacy sake):
uri = URI('https://apistaging.uship.com/oauth/token')
res = Net::HTTP.post_form(uri, 'grant_type' => 'client_credentials', 'client_id' => 'XXXXXXXXXXXXXXXXXXX', 'client_secret' => 'YYYYYYY')
When I print res, it gives me a valid token, which I have tested. It looks like this:
{
"access_token": "AAAAAAAAAAAAAAAAAAAAA",
"token_type": "bearer",
"expires_in": 600,
"refresh_token": "BBBBBBBBBBBBBBBBBBBBB"
}
But where do I run this call? I am guessing maybe some sort of before_filter on any controllers which will use Active Resource? And if so, how to I pass the access_token to that variable #bearer_token in my ActiveResource model. Also, I know that that access token will expire in 10 minutes, so somehow this code will have to know how to run the oauth token again, at the right times, or pass refresh tokens when Active Resource is being used in less than 10 minute intervals. I am very surprised to not find a simple tutorial for this online. If someone feels I should be using a gem to automate this, let me know which one, because everything I've found doesn't appear to work on rails 5.
If you extend ActiveResource like this https://gist.github.com/acherukuri/0f297e145b8242c2e991647c77f1a91e with https://github.com/oauth-xx/oauth-ruby, you would be able to make calls to your services using the OAuth gem instead of ActiveResource but the response can be handed over to ActiveResource so that you will still be able to rescue errors using ActiveResource::ServerError style in your controllers. In this way, your controller will not be polluted with the generation of OAuth tokens and also ActiveResource will generate a new OAuth token only if it's about to expire. (same token will be used for the subsequent calls until it gets expired)
What is the canonical way to encode resource level permissions into a JWT access_token? Or in other words, how do you best encode access to other people's resources?
Is it something like this:
{
scopes: {
me: ['user', 'repo'], // My user
repo123: ['repo'], // Someone else's repo
org541: ['admin', 'repo'], // My org
org206: ['repo:read'] // Someone else's org
}
}
Or like this, with namespaced scope tags (in this case <resource>|<scope>:
{
scopes: ['me|user', 'me|repo', 'repo123|repo', 'org541|admin'... etc]
}
Or something else again?
This applies equally to "roles" or "memberships" or similar tags (and I realise I've mixed the examples above a bit) - the core question remains is how (best) do you distinguish these tags per resource in a single JWT access_token?
I don't know the exact use case you need to implement, but I would probably try to keep the scopes just for API operations. Such as "get a list of repositories". Then a client using the access token can list the repositories it can work with and the resource server verifies the access rights by the username or user groups.
If you wanted to limit the resources available to the client, you could have a scope that would grant access to just a subset (for example just the user's own repositories).
Having resources and their permissions encoded in scopes would make them hard to use (when composing an authentication request, the client would have know resource identifiers) and the permissions may change over the life of the access token.