Access Keycloak REST Admin API using a service account (client credential grant) - keycloak

I like to manage keycloak from my own application:create user & clients, display users & client. As this is not a real user but a machine I would like to use a service account with a client credential grant as proposed in How to get Keycloak users via REST without admin account . To realize this I:
create a realm
inside the real created a client
configured the access type of the client to "confidential" saved and activated the "Service Accounts Enabled" option that will apear after the save.
enable under scopes the client-roles of the "real-management" (see screenshot)
requested an access token with the "username:password" base64 encoded in the header
curl -X POST 'http://accounts.d10l.de/auth/realms/d10l/protocol/openid-connect/token' \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic ZGV2ZWxvcGVyLXBvcnRhbDpmZGRmYzM4Yy05MzAyLTRlZmQtYTM3Yy1lMWFmZGEyMmRhMzc=" \
-d 'grant_type=client_credentials' \
| jq -r '.access_token'
Try to access the users using the access token:
curl -I GET 'http://accounts.d10l.de/auth/admin/realms/d10l/users/' \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxRVdoNENFUjIweDY5SlBCekU4dU9GdXF4R2NVNlVfWmpTNTQ5bmd2QjNjIn0.eyJqdGkiOiI0NDM0ZDFhNS0xZTA5LTQ4MzQtYWI2Yy0zOTk1YmEwMTgxMzAiLCJleHAiOjE1MzY0MzYwMDEsIm5iZiI6MCwiaWF0IjoxNTM2NDM1NzAxLCJpc3MiOiJodHRwOi8vYWNjb3VudHMuZDEwbC5kZS9hdXRoL3JlYWxtcy9kMTBsIiwiYXVkIjoiZGV2ZWxvcGVyLXBvcnRhbCIsInN1YiI6IjliYWI0YWM1LTRiNWMtNGIxOS05ZTc3LWFjOWFmNzlkNzFhZiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImRldmVsb3Blci1wb3J0YWwiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiIyOWM2YWI3Mi05N2RiLTQ2NWUtYTE1Yy03ZWE5NzA0NmZlYzQiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVzb3VyY2VfYWNjZXNzIjp7fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJkZXZlbG9wZXItcG9ydGFsIiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1kZXZlbG9wZXItcG9ydGFsIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4xNy4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1kZXZlbG9wZXItcG9ydGFsQHBsYWNlaG9sZGVyLm9yZyJ9.D_XnpF1rwCayup8h4UXM4AGWkY_xQo40X-yIlWhmqaxkVh1FQy24932VDRCAmxYHcrwazRMqO7snXmre3_8YF5R9Dt8GYjiBorECvQ9X_nBwunmHqnGxIeE64c2GXiz6zSjdgQJQE8fH10NsLyFWHQ-lBPsBwZBsrkKQ5QUEU2qjE7rDRPtYLJPB94BSE4QGfedmRIbvg39snVkClBDUmuBTq_Rc4p7kV69h0a2Mb1sgEr3MdB4RcsOe3gJPZVVtu7gZuGqcAQKMYgtybArF3OXz37w8hjUp6FABxDcvY7K-jsGxXn0hSU0OB7wxAWY9vP4ar4tQYlKxNjs46rPLWw"
But the response is a 403:
url: (6) Could not resolve host: GET
HTTP/1.1 403 Forbidden
content-length: 0
date: Sat, 08 Sep 2018 19:42:06 GMT
How/Is it possible accessing the Admin REST API from a new service account through a client credential grant?

Keycloak differentiates between the Scopes/Scope mapping & the roles management.
The Scopes tab: you see in the question above only manages the roles that a client is allowed to request.
For the client credential grant to work these roles must be assigned to the client in the "Service Account Roles" Tab.
So in the end the client receive a token that is the intersection of both of those configurations.
Source: https://www.keycloak.org/docs/latest/server_admin/index.html#_service_accounts

Related

Personal access tokens with Keycloak

Is there way to create user-specific (aka. personal) access tokens in Keycloak?
So, our goal would be that a user creates one or more personal access tokens (like in GitHub or GitLab) and he/she uses these tokens for authentication.
Any idea how to achieve this using Keycloak?
Keycloak have no short size of personal API key
But you can use full size of JWT.
It is getting Access Token with password grant type.
This is demo
localhost:8180 – is a host and a port number on which the Keycloak server is running,
test – is a Keycloak Realm,
admin-cli – one of default client,
user: user name
1234: password
password : grand_type
The Grant Type is a way to exchange a user’s credentials for an access token.
$ curl -v -d "client_id=admin-cli" -d "username=user" -d "password=1234" -d "grant_type=password" -X POST "http://localhost:8180/auth/realms/test/protocol/openid-connect/token" | jq '.access_token'
result in terminal
"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6Q0hvYVhPX2hqbXFRTnNYOVlacnhkZmpUdk1sbWozUFNkSXpTWUJjMzRFIn0.eyJleHAiOjE2NjAzMzU4ODIsImlhdCI6MTY2MDMzNTU4MiwianRpIjoiYmFkMmFlNjktMWNiYS00NGMyLWEyZDctYzdhOWQ5YjQ4OGI4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJzdWIiOiIzOTRiNjcwOS0zOGEyLTRhYTAtYTg0OS1mZTRjODFlNDA2MjQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiZGM5YjI1YzEtOTRlMy00MTlhLTliOGQtMjYwZTVkMmY3MzcxIiwiYWNyIjoiMSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6ImRjOWIyNWMxLTk0ZTMtNDE5YS05YjhkLTI2MGU1ZDJmNzM3MSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IlRvbSBDdXJpZXMiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIiwiZ2l2ZW5fbmFtZSI6IlRvbSIsImZhbWlseV9uYW1lIjoiQ3VyaWVzIiwiZW1haWwiOiJ1c2VyQHRlc3QuY29tIn0.Gnx6WwpsUxQM990H89BBYi_Kiy3BdAQcCYFRknDJ9qpkGAZSYrAAjjEeEBA5ZXKIUzu0O2trly0iWVW1r4Lb5ihiqSva1BaR42v5WWXHL9gpeS-ttsoKP6N1x74msHXssRmjN5vZG_dwXF-gpKhhJmXI0fLW3-_uuH3LZ9Ar-4zNliVLjJGmePtzq74nn7WbwhggiWDQu2_mQFBnf9vJvcxn6jY9TX5oxppn4BNcvXrChAZyDXLt7RW32ud3p1Cs7Jn360Vg6cRo4wq7-Mams8hhnonhDEaakJtcin7gLK6jwFC7f2ND8banDYL3CEsem4HpmxSRX43UBhfyO64Z8A"
API call for getting user information with this access_token
$ curl --location --request GET 'http://localhost:8180/auth/realms/test/protocol/openid-connect/userinfo' --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6Q0hvYVhPX2hqbXFRTnNYOVlacnhkZmpUdk1sbWozUFNkSXpTWUJjMzRFIn0.eyJleHAiOjE2NjAzMzU4MjMsImlhdCI6MTY2MDMzNTUyMywianRpIjoiYjc3MjMzY2ItYTI2Zi00ZGNiLWJkZTktNjRiNzhiMmQzZTM2IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJzdWIiOiIzOTRiNjcwOS0zOGEyLTRhYTAtYTg0OS1mZTRjODFlNDA2MjQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNjg2Y2EyNDctMzZjYS00MTNiLWE4M2YtODUyZjJmNWY3YWVmIiwiYWNyIjoiMSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjY4NmNhMjQ3LTM2Y2EtNDEzYi1hODNmLTg1MmYyZjVmN2FlZiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IlRvbSBDdXJpZXMiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIiwiZ2l2ZW5fbmFtZSI6IlRvbSIsImZhbWlseV9uYW1lIjoiQ3VyaWVzIiwiZW1haWwiOiJ1c2VyQHRlc3QuY29tIn0.KNioWAOTl7S5bYi303ZWkYRnTv4BnH9B7Q-Oe6j5nO5g_QD2RrWhMO1bUXA3uHdj5P3Y9Mk3vWYfsOlacVfYGmuYmGYhTD6LhbCILPf1r4qMBHUnAtPmWBUmLPNy1XQc-PPjVU6I0FzwW4-UoHCmrqgpDvSoarxf3PiUgwaYzPGm6IeAaHw-ndSO0vkhue2j2s8eVfiiH6TExkngq3zc8E1Y0WK3EM_LxcoDbfrRTNROYDqjK6Bi6_jIECjuRRC6E4ies1svMd8XGwS8iYuHB62hX7_rEBT4PmCwddjfE-IO6XoPy970gdWFUZfiRxbz1Orh-6SySVaeEPdskTGb2Q' | jq
result in terminal
{
"sub": "394b6709-38a2-4aa0-a849-fe4c81e40624",
"email_verified": false,
"name": "Tom Curies",
"preferred_username": "user",
"given_name": "Tom",
"family_name": "Curies",
"email": "user#test.com"
}
You can get the token end point by curl commend.
It is not necessary the access token.
curl --location --request GET 'http://localhost:8180/auth/realms/test/.well-known/openid-configuration' | jq
It will give a full list of result OIDC Discovery
{
"issuer": "http://localhost:8180/auth/realms/test",
"authorization_endpoint": "http://localhost:8180/auth/realms/test/protocol/openid-connect/auth",
"token_endpoint": "http://localhost:8180/auth/realms/test/protocol/openid-connect/token",
"introspection_endpoint": "http://localhost:8180/auth/realms/test/protocol/openid-connect/token/introspect",
"userinfo_endpoint": "http://localhost:8180/auth/realms/test/protocol/openid-connect/userinfo",
"end_session_endpoint": "http://localhost:8180/auth/realms/test/protocol/openid-connect/logout",
>>> deleted here next lines
I think you can customize the authentication flow using a custom Keycloak authentication SPI in that propagates passed personal token to the same 3-rd service that returns if the user is authenticated or not (see https://wjw465150.gitbooks.io/keycloak-documentation/content/server_development/topics/auth-spi.html for details)
Of course, you also need to create some web UI for token creation that will be then stored and verified with this custom SPI.

Keycloak is it possible acess resource server from public client

I am very new to keycloak and I want to know is it possible to create public client then authorize client and get access token. Then use that access token and create UMA tickets to resource server and check if client has rights to access resoucrces using same authorization
I uses this turtorial
https://gruchalski.com/posts/2020-09-05-introduction-to-keycloak-authorization-services/
And it worked if you reauthorized to resource server directly.
But I want to use one authorisation to check rights to resources which could belong to different resource servers
I get access token using this code:
export access_token=`curl --silent -u public-client:1 \
-k -d "grant_type=password&username=${USER_NAME}&password=${USER_PASSWORD}&scope=email profile" \
-H "Content-Type:application/x-www-form-urlencoded" \
${KEYCLOAK_TOKEN_URL} | jq '.access_token' -r`
Then I try to authorise same user to different client:
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=resource-server-1"
But I get "Client does not support permissions"
Maybe someone knows what I need to configure for this to work or even if this can work?
For what I've reading you need two clients. One public and the other confidential.
Then in your client configured as confidential set all your fine-grained authz then in your audience param you set the client id previously configured as confidential.

Keycloak impersonation API not implemented

I've been trying to use the Keycloak Impersonation API (semi-recent addition) to get an access token for another user. I have created a semi-successful CURL request based on the docs and another StackOverflow question. The CURL request (below) returns a 501 Not Implemented and I am trying to figure this out. If it would be another error I would assume I am doing something incorrectly, but this appears to be at least partially correct.
curl --verbose -X POST "http://localhost:8081/auth/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "client_id=admin-cli" \
-d "requested_subject={TARGET_USER_ID}" \
-d "subject_token={USER_MANAGER_TOKEN}"
My workflow so far has been to get the Keycloak master realm "admin" user's access token (successful) and use that in the impersonation request, along with the target user's Keycloak ID. Am I doing something wrong or missing a step?
I haven't changed any Keycloak permissions, is this required?
From my understanding and the documentation, impersonation is currently supported and enabled by default in Keycloak v5 - Sever Installation. However, another article (Keycloak v5 - Token Exchange) seems to indicate that the feature is disabled by default; could this be why I am getting the 501 Not Implemented?
EDIT: #qdivision mentioned that the Token Exchange needs to be enabled for this to work. However, we are using the jboss/keycloak Docker image and I am wondering where I should add the profile.properties file to enable this feature?
Impersonation is enabled by default, Token Exchange is not.
To enable start the server with -Dkeycloak.profile=preview or -Dkeycloak.profile.feature.token_exchange=enabled as mentioned in the docs
https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange

How to get users from Keycloak REST API - Keycloak API response 403

Hi I'm trying to use the Keycloak API but I don't understand very well how it works. I want to obtain all the users of a realm. So I first obtain a token using this endpoint: /realms/master/protocol/openid-connect/token with this params in the request body:
client_id
grant_type
username
password
client_secret
The first question is: What client should I use?
Then I call this endpoint: /admin/realms/master/users with the token in the Authorization header, but I get a 403 status code and I don't understand why.
Thanks
You need two steps
first get an access token from the admin-cli client of the master realm
second call the admin rest api with the access token, set Bearer as prefix in the
Authorization header.
# get an access token
curl -X POST \
https://<HOST>/auth/realms/master/protocol/openid-connect/token \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'cache-control: no-cache' \
-d 'grant_type=password&username=<USERNAME>l&password=<PASSWORD>&client_id=admin-cli'
# get all users of gateway realm, use the token from above and use Bearer as prefix
curl -X GET \
https://<HOST>/auth/admin/realms/gateway/users \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkI...' \
-H 'cache-control: no-cache'
Generally: 403 = you don't have permissions to perform requested action (to view users in this particular case). You need to define Client Roles (realm-management) for used user/client and assign right role (view-users role in this case):
Keycloak 17+ UI:
Or Keycloak 17+ UI - client with Service Accounts Enabled enabled:
Keycloak 17- UI:

How to find the user id and password for IBM API?

I am a beginner for IBM API. I just launched an IBM Natural Language Understanding service. However, what I got is the API key instead of user id and passwords. Like this:
{
"apikey": "••••••••••••••••••••••••••••••••••••••••••••",
"iam_apikey_description": "Auto generated apikey during resource-key operation for Instance - crn:v1:bluemix:public:natural-language-understanding:us-east:a/6514bcdaafbc465498a244edb484cbe5:53e5f23b-f255-4d6c-b48d-cfce09c975b1::",
"iam_apikey_name": "auto-generated-apikey-51f2d016-d3ec-46bc-8be7-496ae621983d",
"iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
"iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/6514bcdaafbc465498a244edb484cbe5::serviceid:ServiceId-d83a34de-5860-443a-817a-b3cb3fb44e2a",
"url": "https://gateway-wdc.watsonplatform.net/natural-language-understanding/api"
}
In the example below, it shows I need a user id and a password. Where can I find them? Thanks!
import json
from watson_developer_cloud import NaturalLanguageUnderstandingV1
from watson_developer_cloud.natural_language_understanding_v1 \
import Features, EntitiesOptions, KeywordsOptions
natural_language_understanding = NaturalLanguageUnderstandingV1(
username='username',
password='password',
version='2018-03-16')
response = natural_language_understanding.analyze(
text='IBM is an American multinational technology company '
'headquartered in Armonk, New York, United States, '
'with operations in over 170 countries.',
features=Features(
entities=EntitiesOptions(
emotion=True,
sentiment=True,
limit=2),
keywords=KeywordsOptions(
emotion=True,
sentiment=True,
limit=2)))
print(json.dumps(response, indent=2))
This is all explained in the getting started tutorial from the instance.
Click Show to view your credentials.
Copy the username, password, and url values.
Important: The tutorial uses service instance credentials to
authenticate to the Natural Language Understanding service. In some
regions, new service instances instead use IBM® Cloud Identity and
Access Management (IAM) tokens for authentication. Authenticate by
using the approach that is right for your region and service instance.
They mention differing authentication types by region - but they don't really specify which regions use which type.
It is pointed out in the release notes
29 May 2018
The service now supports a new API authentication process for service
instances created in Sydney (au-syd). IBM® Cloud is in the process of
migrating to token-based Identity and Access Management (IAM)
authentication. IAM uses access tokens rather than service credentials
for authentication with a service.
As of 29th of May only newly created instance in Sydney (au-syd) use a different authentication method. I'm not sure if there is a better way to find this information out besides crawling through release notes chronologically.
So if your instance was created in the Sydney (au-syd) region after the 28th May 2018 or other regions have since been moved over to this system you'll have to generate a token and pass it through instead.
Using basic auth to intially get the token
curl -k -X POST \
--header "Authorization: Basic Yng6Yng=" \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "Accept: application/json" \
--data-urlencode "grant_type=urn:ibm:params:oauth:grant-type:apikey" \
--data-urlencode "apikey={api_key}" \
"https://iam.bluemix.net/identity/token"
Then using the token from response for further API calls.
curl -X GET \
--header "Authorization: Bearer {token}" \
"https://gateway.watsonplatform.net/discovery/api/v1/environments?version=2017-11-07"
Just keep in mind that you will need to refresh the token periodically.
We added support for IAM in version 1.3.3. Always make sure you are using the latest version.
With IAM you will replace username and password with an iam_apikey parameter from the apikey credential field.
import json
from watson_developer_cloud import NaturalLanguageUnderstandingV1
from watson_developer_cloud.natural_language_understanding_v1 \
import Features, EntitiesOptions, KeywordsOptions
natural_language_understanding = NaturalLanguageUnderstandingV1(
iam_apikey='the apikey value from your question',
url='https://gateway.watsonplatform.net/natural-language-understanding/api',
version='2018-03-16')
response = natural_language_understanding.analyze(
text='IBM is an American multinational technology company '
'headquartered in Armonk, New York, United States, '
'with operations in over 170 countries.',
features=Features(
entities=EntitiesOptions(
emotion=True,
sentiment=True,
limit=2),
keywords=KeywordsOptions(
emotion=True,
sentiment=True,
limit=2)))
print(json.dumps(response, indent=2))
Looks like your app needs to use the API key there to request a bearer token from the Identity and Access Manager, according to the instructions at https://github.com/watson-developer-cloud/node-sdk/blob/master/README.md#authentication and https://console.bluemix.net/docs/services/watson/getting-started-iam.html#iam .