Creating Personal Access Tokens in ForgeRock AM 5.x

Written by Simon Moffat

Personal Access Tokens (PAT’s) are used to provide scoped self-managed access credentials that can be used to provide access to trusted systems and services that want to act on a users behalf.

Similar to OAuth tokens, they often don’t have an expiration and are used conceptually instead of passwords. A PAT could be used in combination with a username when performing basic authentication.

For example see the Sign in to GitHub · GitHub page within Github which allows scope-related tokens to be created for services to access your Github profile.

PAT Creation

The PAT can be an opaque string – perhaps a SHA256 hash. Using a hash seems the most sensible approach to avoid collisions and create a fixed-length portable string. A hash without a key of course wont provide any creator assurance/verification function, but since the hash will be stored against the user profile and not treated like a session/token this shouldn’t be an issue.

An example PAT value could be:

Eg:

f83ee64ef9d15a68e5c7a910395ea1611e2fa138b1b9dd7e090941dfed773b2c:{“resource1” : [ “read”, “write”, “execute” ] }

a011286605ff6a5de51f4d46eb511a9e8715498fca87965576c73b8fd27246fe:{“resource2” : [ “read”, “write”]}

The key was simply created by running the resource and the associated permissions through sha256sum on Linux. How you create the hash is beyond the scope of this blog, but this could be easily handled by say ForgeRock IDM and a custom endpoint in a few lines of JavaScript.

PAT Storage

The important aspect is where to store the PAT once it has been created. Ideally this really needs to be stored against the users profile record in DJ. I’d recommend creating a new schema attribute dedicated for PAT’s that is multivalued. The user can then update their PAT’s over REST the same as any other profile attribute.

For this demo I used the existing attribute called “iplanet-am-user-alias-list” for speed as this was multi-valued. I added in a self-created PAT for my fake resource:

image
Using a multi-valued attribute allows me to create any number of PAT’s. As they don’t have an expiration they might last for some time in the user store.

PAT Usage

Once stored, they could be used in a variety of ways to provide “access” within applications. The most simple way, is to leverage the AM authorization engine as a decision point to verify that a PAT exists and what permissions it maps to.

Once the PAT is stored and created, the end user can present it to another user/service that they want to use the PAT. That service or user presents the username:PAT combination to the service they wish to gain access to. That service calls the AM authorization API’s to see if the user:PAT combination is valid.

The protected service would call {{OpenAM}}/openam/json/policies?_action=evaluate with a payload similar to:

image

Here I am calling the …/policies endpoint with a dedicated account called “policyeval” which has ability to read the REST endpoint and also read realm users which we will need later on. Edit the necessary privileges tab within the Admin console.

If the PAT exists within the user profile of “smoff”, AM returns an access=true message, along with the resource and associated permissions that can be used within the calling application:

image

So what needs setting up in the background to allow AM to make these decisions? Well all pretty simple really.

Create Authorization Resource Type for PAT’s

Firstly create a resource type that matches the pat://. format (or any format you prefer):

image

Next we need to add a policy set that will contain our access policies:

image

The PATValidator only contains one policy called AllPATs, which is just a wildcard match for pat://:. This will allow any combination of user:pat to be submitted for validation:

image

Make sure to set the subjects condition to “NOT Never Match” as we are not analysing user session data here. The logic for analysis is being handled by a simple script.

PAT Authorization Script

At a high level is does the following:

  1. Captures the submitted username and PAT that is part of the authorization request
  2. As the user will not have a local session, we need to make a native REST call to look up the user
  3. We do this by first generating a session for our policyeval user
  4. We use that session to call the …/json/users endpoint to perform a search for the users PATs
  5. We do a compare between the submitted PAT and any PAT’s found against the user profile
  6. If a match is found, we pull out the assigned permissions and send back as a response attribute array to the calling application