Pages

Wednesday, 21 June 2017

Creating Personal Access Tokens in ForgeRock AM

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, or access user data.

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 https://github.com/settings/tokens page within Github which allows scope-related tokens to be created for services to access your Github profile.  Here Github allows you to provide a token that can gain access to services and data stored on Github.  The token is used in conjunction with your username over basic authentication.

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:{“profile” : [ “givenName”, “fullName”, “mail” ] }
a011286605ff6a5de51f4d46eb511a9e8715498fca87965576c73b8fd27246fe:{"profile" : [ "postalladdress", "mail"]}

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 multi-valued.  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:


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" to other users, application, service accounts or personas of yourself.  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 provide it to another user/service that they want to use the PAT.  That service or user presents the username:PAT combination to the protected service they houses the data they want to gain access to.  That service calls the AM authorization API's to see if the user:PAT combination is valid.  A typical resource server to authorization server dance in the OAuth2 world.

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


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:


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):



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



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:




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

The script is available here.

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

Summary

There are any number of ways to create and use PAT's.  Another option for use, could be a custom authentication module that takes the username and hash and perform an authentication test.  The hash in this case would likely need a salt and some other storage protection mechanisms.

A third approach would be to integrate into the OAuth2 world, but this would require a bit more effort, especially with respect to token creation and scope mapping.

No comments:

Post a Comment