When to use Realm roles in Keycloak? - keycloak

I'm experimenting with role mappings among microservices & frontends (keycloak-clients in Keycloak terms).
Let's suppose I have two keycloak clients:
routemanagement-api
routemanagement-webapp
In the routemanagement-api I'd define some roles, let's say one of them: regular-user . This role is not composite role.
In the routemanagement-webapp, I'd define another role, also named regular-user. This is a composite role. I'd associate it with the "regular-role" user in the routemanagement-api.
Then, I create a user. Let's suppose this user registers through routemanagement-webapp. So, my registration logic will assign "routemanagement-webapp:regular-user" role to this newly created user.
Since "routemanagement-webapp:regular-user" is associated with "routemanagement-api:regular-user" role, calls to routemanagement-api REST endpoints will succeed.
You see, I don't need realm (upper-level) roles to make that happen. I can jump from one client to another client directly. I'd say my approach is a top-down approach; the frontend apps at the top, the apis at the bottom. I'm thinking of having separate webapp for provisioning users. User will be granted roles to "webapps" she is allowed to use. The correct permissions to use associated apis are handled in the keycloak UI, by those composite-roles trick.
What do you think of that approach? Is it a correct way of thinking? And what do we need realm roles for?

I'd suggest you might not need the composite role. In my view, the api owns the resource so you should design your client roles as the api as the api client as the resource owner. The role could be named "verb-resource", e.g. "create-x, read-x, update-x, delete-x".
Even if you're authenticating using routemanagement-webapp client you can still infer the resource roles of the user for the routemanagement-api.
EXAMPLE JWT
{
"jti": "0ba58a94-d98e-4f29-abfa-ade95d96a62b",
"exp": 1589264282,
"nbf": 0,
"iat": 1589263982,
"iss": "http://localhost:8080/auth/realms/master",
"aud": "routemanagement-webapp",
"sub": "50b6d1f7-88ea-451c-92bb-fc7f5fcc6683",
"typ": "Bearer",
"azp": "routemanagement-webapp",
"auth_time": 0,
"session_state": "7a270756-917b-4afc-a06d-054b05e6d41a",
"acr": "1",
"allowed-origins": [],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"routemanagement-api": {
"roles": [
"manage-regular-stuff"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"name": "Test User",
"preferred_username": "test.user",
"enabled": true,
"email": "test.user#test.com"
}
If you wanted To get more granular your could use the authorization and resource registration instead of the role based "verb-resource" naming strategy.

Related

Removing cognito:username on AWS Cognito JWT Response

I am using AWS Cognito to conduct authentication. And, I am trying to figure-out if it's possible to remove one of JWT's key, value?
cognito:username
A typical decoded JWT would contain.
{
"sub": "aaaaaaaa-bbbb-cccc-dddd-example",
"aud": "xxxxxxxxxxxxexample",
"email_verified": true,
"token_use": "id",
"auth_time": 1500009400,
"iss": "https://cognito-idp.ap-southeast-2.amazonaws.com/ap-southeast-2_example",
"cognito:username": "exampleuser",
"exp": 1500013000,
"given_name": "Anaya",
"iat": 1500009400,
"email": "exampleuser#example.com"
}
Based here: https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/
I wanted to remove on the accessToken the cognito:username.
Just checking options if it's possible to remove or hide via cognito first, my last option would be using the code, meaning, parse it, then remove, then encode again.
Thanks
cognito:username cannot be removed or modified.
Ref: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html

How to add Keycloak realm role to group via REST API

I want to assign the realm role "TEST_ROLE_123" to a group, I am using
PUT /admin/realms/ataccamaone/groups/{group-id}
{
"realmRoles":["TEST_ROLE_123"]
}
I got group-id from /admin/realms/ataccamaone/groups/
However I get the response 204 No Content and in the Keycloak console I do not see the assignment.
I tried to reproduce your problem and find that PUT /admin/realms/ataccamaone/groups/{group-id} can only edit group name.
Inspect into "Network" tab of browser, I see it uses another URL to map roles to groups. And steps to do this via Admin REST API are:
Obtain PAT as described in https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_protection_whatis_obtain_pat section
Following steps use this PAT as Bearer token (in "Authorization" header). I guess you've already got this.
Call GET http://localhost:8080/auth/admin/realms/realm1/roles to get list of roles, including their name and id values.
Call GET http://localhost:8080/auth/admin/realms/realm1/groups to get list of groups, including their ids
Call POST http://localhost:8080/auth/admin/realms/realm1/groups/{group-id}/role-mappings/realm with following body:
[
{
"id": "9083cac3-4280-497d-b973-7713a5fb12b4", // role-id
"name": "secretary" // role-name
}
]
Call DELETE with URL and body same as step 4 to remove roles from group.
I've faced same issue and corrected it with using a GROUP, Basically I've added the preferred ROLE into the User Groups ROLE LIST and used that specific user group while creating the user via REST API.
Eg:- ADMIN_USER_GROUP -> INCLUDED ('ADMIN_ROLE')
Then User creation API Request should be like below,
{
"firstName": "Sergey",
"lastName": "Kargopolov",
"email": "test4#test.com",
"enabled": "true",
"credentials": [
{
"value": "123"
}
],
"groups": [
"ADMIN_USER_GROUP"
]
}

Minimal jwt token payload with keycloak

At my company, we are trying to use Keycloak to generate a jwt token with an minimal payload with only the role of the user, the email et the expiration of the token.
We need to use this minimal payload because we have a performance limitation I do not wish to explain further.
Is it possible? and how?
We are using Keycloak v7.0.0 and tried different things with the mappers to expose only what we need without success.
Edit: By removing everything in default scope, I still get a lot I do not need
{
"jti": "ac6f9ed1-e33b-4204-affc-c5992c600ead",
"exp": 1571741519,
"nbf": 0,
"iat": 1571741219,
"iss": "http://keycloak:8080/auth/realms/test",
"sub": "c530d809-fabf-44a7-b186-1d095321edf7",
"typ": "Bearer",
"azp": "web_app",
"auth_time": 0,
"session_state": "83ce24ff-9e2f-4775-ae2c-066935b7ffa9",
"acr": "1",
"scope": "openid"
}
I would like a payload that looks like this
{
"exp": 1571741519,
"email": "john#doe.com"
"groups": ["ROLE_USER"]
}
I am looking into adding a authenticator SPI
You should remove everything except role and role_list from Default Client Scopes.
By adding a script mapper, we can remove a lot of info from the payload.
token.setIssuer(null)
removes iss key
token.notBefore(null)
set nbf key to 0
You can look at the token JavaDoc for more information on what is possible to change.

How to create a simple Web Map with ArcGIS REST API

Following the instructions on this page Working with users, groups, and items—ArcGIS REST API: Users, groups, and content | ArcGIS for Developers and the Add Item documentation I was able build a POST request in POSTMAN to add a new item to the user.
After getting the token, when I try the POST request to add the web map I get this error
{"error":{"code":403,"messageCode":"GWM_0003","message":"You do not have permissions to access this resource or perform this operation.","details":[]}}
This is the JSON that contain some simple Web Map data,
{
"operationalLayers": [],
"baseMap": {
"baseMapLayers": [
{
"id": "defaultBasemap",
"layerType": "ArcGISTiledMapServiceLayer",
"url": "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",
"visibility": true,
"opacity": 1,
"title": "Topographic"
}
],
"title": "Topographic"
},
"spatialReference": {
"wkid": 102100,
"latestWkid": 3857
},
"authoringApp": "WebMapViewer",
"authoringAppVersion": "5.4",
"version": "2.11"
}
I was using the wrong access token.
I was using the access token I had for the app I was testing instead of the user's access token that I had to get with oauth2.
I'm leaving this here for future newbies.

Complex claims in JWT

The JWT RFC does not seem to have any problem containing complex arrays such as:
{
"email": "test#test.com",
"businesses": [
{
"businessId": "1",
"businessName": "One",
"roles": [
"admin",
"accountant"
]
},
{
"businessId": "2",
"businessName": "Two",
"roles": [
"support"
]
}
]
}
And this seems a desirable scenario for our needs, since as part of the token we'd like to have a list of businesses a user has access to and what roles does he have for each business (it's part of its identity). The authorization policies at the API would later understand those groups and apply the required authorization logic.
I have seen that with IdentityServer4 the claims are added to the ProfileDataRequestContext's IEnumerable<Claim> IssuedClaims property.
Is there any recommended alternative to this complex claim structure? If not, is there any way to build that structure with IdentityServer4 (maybe some extension?) or the only way would be to manually serialize the JSON since the Claim seems to accept only a string?
PS: I have seen this question and this other where one of the authors of Identity Server talks about something similar being an antipattern. Not sure if the antipattern would be to have complex claims' structure or "authorization implementation details" in the claims.
Any advice on this would be great!
UPDATE:
After giving some thoughts I agree having a complex hierarchy of claims is not desirable and I could go around this problem with a dirty solution of prefixing roles for each businessId. Something like this:
{
"email": "test#test.com",
"roles": [
"1_admin",
"1_accountant",
"2_support"
],
"businesses": [
"1_One",
"2_Two"
]
}
that way I keep a simple structure and later on, at the client or API I can read the claims and find out that 1 is the id for the business with name One and it has the roles admin and account.
Would this be a better solution?
Claims are about identity information - and not complex permission "objects". You are far better off with a dedicated permission service that returns your permissions in any format you want based on the identity of the user.
I also hope your permission data doesn't change while the token is being used, otherwise you end up with stale data.
That said - claims are always strings in .NET - but you can serialize JSON objects into it by setting the ClaimValueType to IdentityServerConstants.ClaimValueTypes.Json.