Keycloak user authorization openid-protocol Rest API - authorization

i am new to keycloak.
I have made web portal that authentication (login, logout,forgot password) of users is done in backend ( PHP ) using REST Api. After successful authentication user is allowed to enter secure part of portal.
I am having trouble to get REST API endpoint so that when user is logged in i would like to get a list of permissions that this user have so i can render the UI with functions that specific user have permissions to. So far i found endpoint which can ask for specific permission only
curl -X POST http://$URL/auth/realms/argo/protocol/openid-connect/token -H "Authorization: Bearer $TOKEN" --data "audience=$CLIENTID" --data "permission=$PERMISSIONNAME#$PERMISSIONSCOPE"
Is this possible with keycloak ? I would have maybe around 10 navigation functions and some will be payable so once user buys this function we will allow this permission to this specific user.
Thanks

I spent a lot of time to make it work.
Basically, once the user is logged in (via a JWT access token) your app has to issue an additional call to an OIDC endpoint, in order to get an extended JWT token (including fine grained permissions).
Here are the details of this extra call:
POST http://server:port/auth/realms/<realm>/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
Authorization: "Bearer ....." (=access token of logged-in user)
Parameters:
- grant_type: the constant "urn:ietf:params:oauth:grant-type:uma-ticket"
- audience : the keycloak client id
- response_include_resource_name: true
You will get in response a JWT token that should be decoded
either programatically (quite easy)
or by invoking Keycloak token introspection endpoint (ie /auth/realms//protocol/openid-connect/token/introspect
And, once decoded, you will notice that the json payload contains an extra "authorization" node.

Related

Keycloak: Authorization between services and the public frontend

I have an application which consists of a frontend and several backend services. The authentication is done via Keycloak.
The workflow looks like this:
The user logs into the frontend and gets a token from Keycloak. This token is sent to the backend with every request.
The following image explains the current architecture:
In Keycloak I have the following clients:
1. Frontend
Access Type: public
Client Protocol: openid-connect
2. Core Service
Access Type: bearer-only
Client Protocol: openid-connect
3. User Service
Access Type: bearer-only
Client Protocol: openid-connect
How can I validate calls between services now?
I would imagine something like a service account and these have the possibility to call each other independently from the bearer-token from the frontend. The problem is that both services can be called from the frontend as well as between each other.
Edit:
My API is written with NestJS.
The API of the user-service:
And this is how I call the user-service in my core-service:
and this is my keycloak configuration for the the user-service:
At the moment I don't add anything to the request and I don't have any extra configuration on the interface. So I added the #Resource('user-service')-Annotation to the Controller and the #Scope()-Annotation to the Endpoint.
After that I don't get an error immediately and the endpoint is called.I can log that the logic is executed. But as response I still get a 401 Unauthorized Error.
Do I need to specify a scope or what do I need to add in the #Resource-Annotation?
Edit 2:
I'll try to show you my current situation with many screenshots.
Initial situation
Here is your drawing again. For me, points 1-5 work and point 8 works even if I do not call another service.
My Configuration
That this works, I have the following configuration:
Just Frontend and Core Service
Frontend:
Core-Service:
For the core service (gutachten-backend), I do not need to make any further configurations for this. I also have 2 different roles and I can specify them within the API.
Using Postman I send a request to the API and get the token from http://KEYCLOAK-SERVER_URL/auth/realms/REALM_NAME/protocol/openid-connect/token.
These are my 2 testing methods. I call the first one and it works. The following is logged. Means the token is validated received and I get Access:
Calling the user service
Now I call the second method. This method calls the user-service.
This is my request in the core-service:
I do not add anything else to my request. Like a bearer token in the header.
The endpoint in the user service is just a test method which logs a message.
This is my configuration for the user service:
I have now tried something with resources, policies and permissions.
Resource
Policies
Role-Policy
Client-Policy:
Permission
And analogously the client permission
Questions and thoughts
All steps from the first drawing seem to work except 6 and 7
Do I need to add more information to my request from core service to user service?
How to deal with root url and resource urls?
In the code in the API, do I need to additionally configure the endpoints and specify the specific resources and policies? (NestJS offers the possibility to provide controllers with a #Resource('<name>') and endpoints with #Scopes([<list>]))
Additionally, through a tutorial on setting up keyacloak in NestJS, I turned on the following config:
This adds a global level resource guard, which is permissive.
Only controllers annotated with #Resource and
methods with #Scopes are handled by this guard.
Keycloak's Token Verification API can do it.
This is one of Architecture for Authorization of resource access permission.
Between Core Service and User Service, Core Service needs to verify the access-token to Keycloak.
It means this token can access the User service API Yes(Allow) or No(Deny)
This is API format
curl -X POST \
http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
-H "Authorization: Bearer ${access_token}" \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience={resource_server_client_id}" \
--data "permission=Resource A#Scope A" \
--data "permission=Resource B#Scope B"
Demo Keycloak Token URL: localhost:8180
Authorization Enabled Realm: test
Authorization Enabled Client: core-service
Client Resource: resource:user-service
User1 : can access it (ALLOW) password: 1234
User2 : can access it (ALLOW) password:1234
Steps
Get User Access Token(instead of login) ->
Preparations
ready to assign access-token(named user-token) variable in Postman
var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("user-token", jsonData.access_token);
Get Token URL from Keycloak UI, click the Endpoints
Get User1's access token
with Bearer Token option with {{user-token}} in Authorization Tab
Verify with user1 token from Core Service to Keycloak
return 200 OK from Keycloak (ALLOW) - it is Circle 4 and 5 in my Architecture.
So Core Service forward API call to User Service for accessing service
Note - needs to finish Keycloak Permission setting
Verify with user2 token from Core Service to Keycloak
return 200 OK from Keycloak (Allow) too.
So Core Service return an error to Front-end, like this user can't access a resource of User Service.
More detail information is in here
Keycloak Permission setting
Create Client
Create Client Resource
Add Client Role
Add Client Policy
Add Permission
All user mapping into Client role
This is Configuration in Keycloak
Create Client
Create Client Resource
Add Client Role
Add Client Policy - role based
Add Permission
All user mapping into Client role - any user if you want to add to access the resource.
For people who have the same problem in the future. The answer from #BenchVue helped a lot to understand the concept in general.
What was missing is that a token must also be added for each request between services. Namely the token of the client.
So before the request is sent, the following query takes place. This is the method to get the token for a client:
getAccessToken(): Observable<string> {
const header = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
};
return this.httpService.post(
'{{keycloakurl}}/auth/realms/{{realm}}/protocol/openid-connect/token',
`grant_type=client_credentials&client_id={{clientId}}&client_secret={{clientSecret}}`,
header).pipe(
map((response) => {
return response.data.access_token as string;
}
));
}

How to retrieve AccessToken in Auth0 for a specific user to deal with a permissioned REST API?

I am using Auth0 for User-Authentication in my VueJS-Application.
Now I want to create a backend providing a REST-API that will also use OAuth2-protocol and JWT-tokens in conjunction with our Auth0-based Frontend. This basically means: after SignUp of a new User, I want to send this new User to our backend in order to create a new record (which is already done).
The REST-API will be only accessible with a valid JWT-Token, which works perfectly following some tutorials and examples provided by Auth0 like this one.
I'd like to validate an access_token on my backend with Auth0 for a user, that was authenticated on the frontend application using auth0 also in the backend without sending any passwords.
This way, the Frontend or any other client can obtain an Access-Token like with this curl-command:
curl --request POST \
--url https://dev-xxxxx.eu.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{"client_id":"DLjQxxxx","client_secret":"*****","audience":"https://api.example.org","grant_type":"client_credentials"}'
On the backend, I am able to parse this token against Auth0 to check if this token is valid or not.
But how can I obtain a token on the Frontend for a specific user consisting of its email and password in Auth0-authentication? The Auth0-API will not return any access token after user authentication that I can use together with my API in order to validate the JWT token for specific roles.
I think I don't get the final workflow here.
Can someone point me please into the right direction?

How to get new refresh Google OAuth token

I've got some code (a script on a server) that tries to send an OAuth2 request to get a token from an API. I have a client id, and client secret from the "OAuth 2.0 Client Ids" section of the "Credentials" tab in the Google Cloud Platform > APIs and Services. I also have a refresh token that I originally obtained somehow.
The URL I am POSTing to is:
https://www.googleapis.com/oauth2/v4/token
I'm sending the header
Content-Type: application/x-www-form-urlencoded
In the body of my post I have the following information:
grant_type=refresh_token&client_id=${encodeURIComponent(client_id)}&client_secret=${encodeURIComponent(client_secret)}&refresh_token=${encodeURIComponent(refresh_token)}
However, it has been a long time since I last ran this code and now it returns an error "bad grant". On this page it says that a refresh token will stop working if it has not been used for six months, which explains why I am getting the error. However, it does not say how to get another refresh token using the client id and client secret similar to how I am now creating a post to get an access token. How do I do this?
I believe your goal and your current situation as follows.
You want to retrieve new refresh token from the current client ID and client secret.
Your client ID and client secret are the valid values.
In this case, in order to retrieve new refresh token, it is required to use the additinal 2 parameters of scope and redirect_uri. These parameters can be confirmed at your created client ID of "OAuth 2.0 Client IDs" of "Credensials" tab in the Google Cloud Platform. When the parameters including client_id, client_secret, scope and redirect_uri are used, new refresh token can be retrieved. The flow for this is as follows.
1. Retrieve authorization code.
Please create the following endpoint using client_id, redirect_uri and scope.
https://accounts.google.com/o/oauth2/auth?client_id={your client ID}&redirect_uri={your redirect uri}&scope={your scopes}&response_type=code&approval_prompt=force&access_type=offline
When you created above endpoint, please access it to your browser. By this, the login screen is opened. When you logged in to Google account, the authorization screen is opened. When you permit the scopes, the authorization code can be retrieved.
When your credential is for the web application, you can retrieve the code at the URL on the browser like http://{your redirect uri}/?code={the authorization code}&scope={your scopes}.
Please copy the code.
2. Retrieve refresh token.
Using the retrieved authorization code, you can retrieve new refresh token. The sample curl command for this is as follows.
curl \
-d "client_id={your client ID}" \
-d "client_secret={your client secret}" \
-d "redirect_uri={your redirect uri}" \
-d "grant_type=authorization_code" \
-d "code={retrieved your authorization code}" \
"https://www.googleapis.com/oauth2/v4/token"
When above curl command is run, the following result is obtained.
{
"access_token": "###",
"expires_in": 3600,
"refresh_token": "###",
"scope": "{your scopes}",
"token_type": "Bearer"
}
Reference:
Using OAuth 2.0 to Access Google APIs

How to use "user context access token" that I get from Twitter OAuth 1.0a in my request?

I have successfully (?) implement the Twitter three-legged authentication process to obtain user access token. The problem is the access token appears invalid... or I use it wrong. I already able to get the app's access token, which can access limited Twitter API. I use it by adding "Authentication: Bearer 'access token'" on the header. But when I did the same thing with the user context access token and did the same request, I always get error code 89 Invalid or expired token.
The access token I obtained has a structure of [several numerics]-[some alpha numeric chars]. Like 12345678-asd98f798asdf79asdfa9sdfs9df7a9sdf7. This looks similar with the access token example in step 3 of https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens.
I also notice that the example request there is like this:
POST statuses/update.json
oauth_consumer_key=cChZNFj6T5R0TigYB9yd1w
oauth_token=7588892-kagSNqWge8gB1WwE3plnFsJHAZVfxWD7Vb57p0b4
Which I presume those two additional parameters are to be added to the body instead of the header. But, how if my request is a GET request? Like request to get home timeline, which absolutely requiring user context access token?
https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-home_timeline
From this API ref, the example only give the GET url, and not how to supply the user context access token. Please help. I have the feeling that the solution is very simple (like a fix on the header), but I can't see it.
This is my current request:
curl -X GET \
'https://api.twitter.com/1.1/users/show.json?screen_name=huffpost' \
-H 'Authorization: Bearer 12345678-as3d12a3d1a3sd1232ads13asd123as1d23as3d32,Bearer 12345678-as3d12a3d1a3sd1232ads13asd123as1d23as3d32' \
This is the result:
{
"errors": [
{
"code": 89,
"message": "Invalid or expired token."
}
]
}
The user access token requires signing a request which includes parameters and headers.
https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/authorizing-a-request
You can use a library like https://github.com/twitter/joauth to generate the signature.
For Java+OkHttp - you can use this library https://github.com/yschimke/okurl/blob/master/src/main/kotlin/com/baulsupp/okurl/services/twitter/joauth/Signature.kt#L33

Unable to generate refresh token for IBM Visual Recognition API

I am trying to work with IAM token based authentication. I am able to generate 'access token' and could do operations using the 'access token'. Now I am having issue while generating 'refresh token'. I am following this link https://cloud.ibm.com/docs/services/watson?topic=watson-iam.
I am using below command to generate refresh token. Here Authorization header value obtained using username as 'apikey' and value as my key. {refreh-token} value I am using which I received while generating 'access token'.
curl -k -X POST --header "Authorization: Basic Yng6Yng=" --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token={refresh-token}" "https://iam.bluemix.net/identity/token"
I expect to get refresh token but get error {"context":"requestId":"021c3482...""},"errorCode":"BXNIM0507E","errorMessage":"For OpenID Connect related APIs, you need to send your client credentials as basic authorization header"}
Can some one help me in understanding what is going wrong
The description in https://cloud.ibm.com/docs/services/watson?topic=watson-iam is incorrectly describing the refresh case. I will follow-up with the docs team to update that section.
This is the generic description how to get tokens for API keys:
https://cloud.ibm.com/docs/iam?topic=iam-iamtoken_from_apikey
For API usage, IBM Cloud allows you to generate access token's without providing a client id / secret. In this case, a default client id is assumed which is only allowed to create tokens for API keys, but not authorized to use any other grant type - including the grant_type refresh_token. Therefore, simply dismiss the refresh token from the response of the API key grant in the first call - you won't be able to use it.
In the API key use case, there is no benefit of using the grant_type refresh_token over getting a new access token with the API key grant type anyway - all validation steps that are done internally (does the user stil exist / is the user still in the account / ...) are identical. But the refresh token eventually will expire - the API key not.