How to specify client-specific custom attribute in access token - keycloak

I have a Keycloak setup and I'm using the client_credential flow to create access tokens for thousands of back-ends.
I would like to include another custom field in the access token, so next to exp etc., I'd like to have a field publicKey. This field is specific and unique for each of the clients.
Is that possible using Keycloak?
EDIT: Clarified that I have many back-ends and each of them has a specific and unique publicKey

I assume you have a hard-coded value inside the claim publicKey. If so, this will suffice:
You create a custom scope who issues the token. So assuming you have a Client Backend, which wants to exchange a user token for a serviceaccount-token to reach out to a downstream service client_credentials_service, and this serviceaccount-token should have the publicKey attribute inside, here's one way you could do it:
In your realm, create and save a new Scope under Client Scopesin admin UI.
Go to Client Scopes -> publicKeyScope -> Mappers -> create and create a new hardcoded claim, example below:
Open your client client_credentials_service and switch to tab Client Scopes
Here you could add your new scope either as default or as optional scope to your client. optional means, in the request you make to kcs token endpoint to obtain a token via client_credentials flow, you have to add the scope=publicKeyScope param. e.g. like this:
curl --location --request POST 'http://localhost:8080/auth/realms/yourRealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client_credentials_service' \
--data-urlencode 'client_secret=your-secret' \
--data-urlencode 'scope=email publicKeyScope' \
--data-urlencode 'grant_type=client_credentials'
When it's added as default, you don't have to explicitly add the desired scope to the request.
As long as you're not adding this scope (or the corresponding mapping) to another client, only your client_credentials_service token obtained via client credentials flow will include this claim.
Example for resulting token:

Related

How to get custom attributes for a user in Keycloak using the RESTful API?

I would assume this to be straight forward but I can't find it in the docs.
The following curl command:
curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
'https://$URL/auth/realms/$REALM/protocol/openid-connect/userinfo'
results in:
{
"sub": "8182...415",
"email_verified": true,
"name": "n.a. n.a.",
"groups": [],
"preferred_username": "foo#example.com",
"given_name": "n.a.",
"family_name": "n.a.",
"email": "foo#example.com"
}
How do I get the custom attributes for a user?
You can get the user attributes with the get users endpoint from Admin Rest API:
GET /{realm}/users
with the query parameters, exact=true and username.
Step-by-Step:
You can get that information using the Keycloak Admin REST API; to call that API, you need an access token from a user with the proper permissions. For now, I will be using the admin user from the master realm:
curl https://${KEYCLOAK_HOST}/auth/realms/master/protocol/openid-connect/token \
-d "client_id=admin-cli" \
-d "username=$ADMIN_NAME" \
-d "password=$ADMIN_PASSWORD" \
-d "grant_type=password"
You will get a JSON response with the admin's token. Extract the value of property access_token from that response. Let us save it in the variable $ACCESS_TOKEN for later reference.
To get the user attributes from your realm $REALM_NAME:
curl -X GET https://${KEYCLOAK_HOST}/auth/admin/realms/${REALM_NAME}/users/?username=${USERNAME}&exact=true \
-H "Content-Type: application/json" \
-H "Authorization: bearer $ACCESS_TOKEN"
From the response extract the user attributes for example as follows:
jq -r .[].attributes
To retrieve custom user attributes via the userinfo endpoint you need to create a protocol Mapper for the client used to authenticate the user.
That mapper can also be created with the Keycloak Admin rest API. For a more detailed answer on how to create Protocol Mappers for user-attributes (including for the old and new Keycloak APIs) please have a look at the this SO answer.
Or you can do it via Keycloak Admin UI as follows, in the Keycloak go to:
Select your realm
Go to clients
Select the appropriate client for your use-case
(For the OLD Keycloak UI)
Go to Mappers
Click Create
Select Mapper Type as User Attribute
Fill up the field User Attribute with your custom user attribute
Set to be added to the userinfo endpoint
Fill up the remaining fields, accordingly
Click on Save
(For the NEW Keycloak UI)
Go to the tab Client Scopes
Click on the scope -dedicated (e.g., test-dedicated in my example)
Click on Configure a new mapper (or Add Mapper > By configuration if you have already created mappers before for this client)
Select User Attribute
Fill up the field User Attribute with your custom user attribute
Set to be added to the userinfo endpoint
Fill up the remaining fields, accordingly
Click on Save
This is enough to enabled your custom user attribute to be retrieved from the userinfo endpoint

Keycloak Huge JWT Token of master admin

We have a realm per customer, multi-tenant architecture. (Expected to have around 500 realms) There is a service account we use, a client in the master realm that will manage the customer realms. The problem is the huge JWT token for the master realm admin user/service account, who manage all the realms that increases in size as the number of realms increases. This is due to the 20+ client roles of each new realm. What are the different options we have to keep the token size low?
EDIT:
ps: Reducing roles is not an option either. The service account is an admin of the keycloak admin portal and needs to manage all the realms, so it needs manage-realm etc roles for all the realms. Keyclaok admin portal will not for example allow to delete the realm if it doesnt have delete-realm role.
To reduce the size of the token you can use the following strategies:
restrict the roles that the client access;
use client scopes to narrow down the claims that will be added to the token.
For the first option, you can go to the client in question, tab Scope, disable Full Scope Allowed, and then choose only the roles that the client is really interested.
For the second option, you can go to Client Scopes, create a scope, save it, then go to the Scope tab, add the roles that will be part of that scope. Then everytime you make a request for a token to the client in question you can send the scope as a parameter of that request. This way you will only include in the token the roles that belong to that scope.
I think you are running into this https://issues.redhat.com/browse/KEYCLOAK-1268.
If you look through the discussion there you will see
Tokens created for admin-cli or security-admin-console no longer have
any roles embedded within them. The Admin REST API now checks the
"aud" claim. If the audience is one of those clients, then it ignores
claims in token and just accesses the UserModel directly to determine
if admin has specific permissions.
Use admin-cli or security-admin-console for your client_id. Otherwise you will get every role there is for every realm there is in your token.
In order to use either one of those clients with a token request, you have to make it confidential and enable Direct Access Grants.
Then something like this will work and give you a managable token
curl -i -X POST \
-H "Content-Type:application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=admin-cli" \
-d "client_secret=<admin-cli-secret>" \
-d "username=my_admin_user" \
-d "password=<my_admin_user_password>" \
'http://master.foo.com/auth/realms/master/protocol/openid-connect/token'

Change configuration for not change token of private repository

I have a private repository and access of the raw.githubusercontent.com by API is using the ?token=AEDIQE3IPAPDAXI6QPVEBALBSAPEU in the end of the file name. But this token change during the time (10 -15 days) and this is not so good for my purposes. I don't find any way to do not change the token information. Please, this kind of configuration is possible?
Since that token can change, you might consider creating a Personal Access Token (PAT), and downloading the files using the Authorization header instead of a token in the URL.
curl -H "Authorization: token ${PAT}" \
https://raw.githubusercontent.com/user/repo/main/file.txt
The other approach seen here would be, still with a PAT, to
curl -H "Authorization: token ${PAT}" \
https://github.com/<username>/<reponame>/raw/<branch>/<path-to-your-file>
This will return a “redirect (HTTP 302)” with location header value pointing to the URL with the token.
You can get the current "raw.githubusercontent.com" token that way.

Keycloak - How to request a token with a custom lifespan?

Context: We are using Keycloak to secure our APIs by usually passing tokens through Authorization Headers. However, these APIs also allow users to download files (for instance: https://api.service.io/users.xlsx).
To use these "download endpoints", our web client applications pass users' token via query strings. (e.g. https://api.service.io/users.xlsx?accessToken=${bearerToken})).
Problem: Passing tokens via query string has several security flaws (browser history, ...). Therefore we would like to pass a very short-lived token (e.g. lifespan of 15sec) instead of the normal one (lifespan of 300sec by default).
Question: How could we request a different token from Keycloak API (for instance, /realms/#{realm_id}/protocol/openid-connect/token) by:
providing the normal access token (not credentials);
and specifying a different lifespan ?
After reading Keycloak's source code, it appears this is not possible (version 3.4.2.Final) to ask for a specific lifespan at runtime.
However, I developed a Keycloak Custom REST endpoint to do that. https://github.com/looorent/keycloak-configurable-token-api
When this JAR file is deployed in Keycloak, you can ask for a given lifespan at runtime. For example:
$ curl -X POST -d '{ "tokenLifespanInSeconds": 20}' -H "Content-Type: application/json" -H "Authorization: Bearer <user-access-token>" http://auth.service.io/auth/realms/a-realm/configurable-token

Facebook Graph API does not verify appsecret_proof correctly

I have a server application that receives a user access token from the client and generates an appsecret_proof. I would like to make a graph API call in order to verify that the received access token is valid:
curl \
-F 'access_token=<my access token>' \
-F 'appsecret_proof=<my appsecret_proof>' \
https://graph.facebook.com/me
However, when I omit the appsecret_proof field the request is still verified. In both cases the response is:
{"success":true}
If I intentionally pass an invalid appsecre_proof it works as expected:
{"error":{"message":"Invalid appsecret_proof provided in the API argument","type":"GraphMethodException","code":100}}
I have made the neccessary changes in the Advanced settings:
According to the Facebook documentation: "When this is enabled, we will only allow API calls that either include appsecret_proof or are made from the same device the token was issued to."
"Once you've changed that setting, an attacker will not be able to use stolen access tokens without access to your app secret."
What am I missing here?
appsecret_proof is not same as the APP SECRET KEY.
Based on FB documentation, this is how it needs to be obtained:
go to http://www.freeformatter.com/hmac-generator.html#ad-output
in the message area, put the access token
in the secret key, put the APP secret key
Choose SHA256 as the digest algorithm
Generate the HMAC
You can use this generated HMAC as the my appsecret_proof in (for example):-
curl \
-F 'access_token=<my access token>' \
-F 'appsecret_proof=<my appsecret_proof>' \
-F 'batch=[{"method":"GET", "relative_url":"me"},{"method":"GET", "relative_url":"me/friends"}]'
https://graph.facebook.com/me
Omitting the appsecret_proof field might help but in certain regions, FB API demands for it and this is how you need to provide it