Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial JWT token at token refresh

In a microservice architecture, we use JWT tokens from keycloak. Now we would like to get a second access token with less rights (less claims/ less roles). The use case is: the new access token should give its owner access to just one document in the document store. Why? To limit the damage someone could do if he can steal this token.

Ideally, we could get this second token via a special refresh_token call (the user holding the refresh token has the right to get a full access token, so he should also be able to get a partial access token). How could we do this?

Using scopes does not seem to work: the list of given scopes are only evaluated at login (so at the moment of refreshing a token, I cannot adopt the list of scopes).

I also tried to understand https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_overview or RPTs. But unfortunately, I am missing some documentation (and my tries failed).

Are there other ideas? Or maybe even an example showing how to do this?

Later edit to make my question about RPTs more explicit: https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_overview says:

... Keycloak Authorization Services provide extensions to OAuth2 to allow access tokens to be issued based on the processing of all policies associated with the resource(s) or scope(s) being requested. This means that resource servers can enforce access to their protected resources based on the permissions granted by the server and held by an access token. In Keycloak Authorization Services the access token with permissions is called a Requesting Party Token or RPT for short.

Could such an access token with permissions be used for our goal?

In my experiments I could get token with a grant_type=urn:ietf:params:oauth:grant-type:uma-ticket . But there were some issues:

  • I had to change some settings in keycloak to enable permissions (before it would say "Client does not support permissions"). After I made these changes, my normal login call would no longer work (I could test while my token was still valid). I had to scratch my keycloak config to continue working.

  • I do not really understand the permission model to use for this feature

An end-to-end example would be useful (the ones in the Keycloak documentation are a bit abstract).

like image 663
Philipp Avatar asked Feb 07 '19 13:02

Philipp


1 Answers

I have gone into the docs and what you want could be achieved, by protecting your resource server (your application) in order to act as UMA-Protected Resource Server. Here you have a basic example of what could be achieved with this:

Keycloak is a UMA 2.0 compliant authorization server that provides most UMA capabilities.

As an example, consider a user Alice (resource owner) using an Internet Banking Service (resource server) to manage his Bank Account (resource). One day, Alice decides to open her bank account to Bob (requesting party), a accounting professional. However, Bob should only have access to view (scope) Alice’s account.

As a resource server, the Internet Banking Service must be able to protect Alice’s Bank Account. For that, it relies on Keycloak Resource Registration Endpoint to create a resource in the server representing Alice’s Bank Account.

At this moment, if Bob tries to access Alice’s Bank Account, access will be denied. The Internet Banking Service defines a few default policies for banking accounts. One of them is that only the owner, in this case Alice, is allowed to access her bank account.

However, Internet Banking Service in respect to Alice’s privacy also allows her to change specific policies for the banking account. One of these policies that she can change is to define which people are allowed to view her bank account. For that, Internet Banking Service relies on Keycloak to provide to Alice a space where she can select individuals and the operations (or data) they are allowed to access. At any time, Alice can revoke access or grant additional permissions to Bob.

Then use policy enforcers in order to trigger this protection:

If a resource server is protected by a policy enforcer, it responds to client requests based on the permissions carried along with a bearer token. Typically, when you try to access a resource server with a bearer token that is lacking permissions to access a protected resource, the resource server responds with a 401 status code and a WWW-Authenticate header.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="${realm}",
    as_uri="https://${host}:${post}/auth/realms/${realm}",
    ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de"

Here there are two parts you need to do. The first would be to add policy enforcement for the paths of application you want to protect. Then, and here it comes the sauce, you'll need to configure the UMA part. The good part about UMA is that it adds an extra ticketing system to the authorization process, and this tickets are assigned per resource (in fact, they're assigned when you try to access a protected resource).

Client requests a protected resource without sending an RPT

curl -X GET \
  http://${host}:8080/my-resource-server/resource/1bfdfe78-a4e1-4c2d-b142-fc92b75b986f

The resource server sends a response back to the client with a permission ticket and a as_uri parameter with the location of a Keycloak server to where the ticket should be sent in order to obtain an RPT. Resource server responds with a permission ticket

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="${realm}",
    as_uri="https://${host}:${post}/auth/realms/${realm}",
    ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de"

So the client asks for a resource and it is given a ticket with a location of a Keycloak server in order to exchange that ticket for a RPT. This is the client POSTing the token endpoint then, in order to get the RPT:

curl -X POST \
  http://${host}:${port}/auth/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "ticket=${permission_ticket} \
  --data "submit_request=true"

This will give you the RPT which is valid only to access the resource you asked the first time. Say this:

{
  "authorization": {
      "permissions": [
        {
          "resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7",
          "resource_set_name": "Hello World Resource"
        }
      ]
  },
  "jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
  "exp": 1464906971,
  "nbf": 0,
  "iat": 1464906671,
  "sub": "f1888f4d-5172-4359-be0c-af338505d86c",
  "typ": "kc_ett",
  "azp": "hello-world-authz-service"
}

You will also need to manage users access to their resources. Here it is done using the admin UI, but you might need to properly configure it from your application, calling the Keycloak API.

like image 138
Xtreme Biker Avatar answered Nov 07 '22 21:11

Xtreme Biker