Access and Authentication

OAuth: Custom token attributes

Share this post:

OAuth: Custom token attributes

OAuth providers often provide extended functionality to clients, depending on individual requirements. This extended functionality often requires additional information to be stored with an OAuth grant.

This article is going into how ISAM allows you to store additional information and metadata against an OAuth grant. The number of scenarios which can be targeted with this capability are numerous, this is a powerful and flexible feature.

OAuth grants and state ID

In OAuth, there are two tokens we’re very familiar with; the access token and refresh token. The act of obtaining an access token or refresh token is known as a grant. Access tokens are usually short lived and expire, with a refresh token being used to get a new access token. When a refresh token is used, both a new access and refresh token are created, rolling over the token values. Due to the rolling nature of these tokens, there is the need for an additional ID. This ID will be the same for both an access and refresh token, and will not change when a refresh token flow is performed and new tokens are issued. This ID is known as the state ID, which identifies a particular OAuth grant for its lifetime.

This state ID is used heavily when managing extra attributes, as it is the only consistent identifier for a given OAuth grant.

The state ID gets issued when the initial grant is created. So it is available in most OAuth use cases.

Mapping Rules

OAuth extra attributes are managed and used in mapping rules. These occur at two well defined locations during OAuth requests:

Pre-token – The “before” mapping rule, runs before the primary operation of the request such as issuing tokens. Hence the name “pre-token”
Post-token – The “after” mapping rule, runs after the primary operation of the request. This rule usually has the state-id pre-populated and available.

There are some instances when a state_id does not yet exist for a grant during the pre-token invocations. This occurs on a request to /authorize or on a request to /token when doing the ROPC grant type, as no tokens exist yet. Information available in that rule can still be saved against a grant, it just needs to be stored in the stsuu to be retrieved and associated against the grant in the post-token rule of the same request.

While the pre and post token rules are the primary mapping rules in which extra token attributes are used they can also be used in an infomap, however the state_id will have to be manually retrieved.

Mapping a token to a state ID

In mapping rules, its possible to retrieve a token and the information associated with it using the call

OAuthMappingExtUtils.getToken(tokenString);

This will return a token object which contains all the information about that token. Including:

  • Lifetime
  • Username
  • Client ID
  • Scope
  • State Id

state_id is the focus here. The following snippet shows getting a state_id from a token:

var token = OAuthMappingExtUtils.getToken(tokenString);
if(token != null) {
    var state_id = token.getStateId();
}

This is very useful in the pre-token rule, where the state_id isn’t automatically populated, or in an infomap, where the OAuth token has been provided via other means (eg posted as a parameter, present in a header, or in the users credential).

Attribute operations

Now that we’ve explored were a grants attributes can be used and how to get the state_id, we can look at the actual calls which can be made in JavaScript to work with attributes.

There are two classes of operations, single attribute operations, and batch operations. Batch operations were added in 9.0.5.0 and are essential when working with multiple attributes, as they are significantly more efficient with database transactions (one per operation, rather than one per attribute).

Save or update a single attribute with:

OAuthMappingExtUtils.associate(state_id, "attribute_key", "attribute_value");

Remove a single attribute from a grant this will return the value removed if any:

var removed_value = OAuthMappingExtUtils.disassociate(state_id, "attribute_key");

Retrieve a list of all association keys, retrieveAllAssociations is usually more useful, as it fetches the value at the same time – returns a java array of strings

var keys = OAuthMappingExtUtils.getAssociationKeys(state_id);
for(var i = 0 ; i < keys.length; i++) {
    var key = keys[i];
    //Perform operations with each key here
}

Get a specific token attribute – returns a string / null if no value found

var attribute_value = OAuthMappingExtUtils.getAssociation(state_id, "attribute_key");

Get all of the associations – returns a java Map

var attributes = OAuthMappingExtUtils.retrieveAllAssociations(state_id);
var keys = attributes.keySet().toArray();

for (var i = 0; i < keys.length; i++) {
  var key = keys[i];
  var value = attributes.get(key);
  // Do operation with key + value here
}

Add several attributes – will not update existing attributes

var to_add = OAuthMappingExtUtils.getEmptyMap();
to_add.put("attribute1","value1");
to_add.put("attribute2","value2");
OAuthMappingExtUtils.batchCreate(state_id, to_add)

Update several attributes – will not create attributes

var to_update = OAuthMappingExtUtils.getEmptyMap();
to_update.put("attribute1","new_value1");
to_update.put("attribute2","new_value2");
OAuthMappingExtUtils.batchUpdate(String stateID,Map<String, String> attributesToUpdate)

Delete several attributes

var to_delete = new ArrayList();
to_delete.add("attribute1");
to_delete.add("attribute2");
OAuthMappingExtUtils.batchDelete(state_id, to_delete);

It should be noted that attributes do have some limitations, the maximum attribute key or value length is 256. If bigger things need to be stored they must either go in the IDMappingExtCache, or be split up and stored in multiple attributes.

All of the methods which are mentioned here are documented in javadoc which can be downloaded from the appliance.

A common pattern, sharing state from /authorize to /token

One of the most common patterns seen, is the need to share some information between the /authorize and /token endpoints. In a browser based OAuth flow only /authorize will ever have access to the users session, and thus their credential attributes. Token attributes are very useful for saving that value at the /authorize step and then retrieving them at /token. This can be used to add claims to a JWT, or to return additional values in the bearer token which is returned to the client.

The following snippet saves the users authentication level at /authorize, and returns them at /token. It should be added to the post token rule:

if(request_type == "authorization") { // Request to /authorize
  // Get the attribute value to save.
  var to_save = stsuu.getAttributeContainer().getAttributeValueByName("AUTHENTICATION_LEVEL");
  // Save the attribute
  OAuthMappingExtUtils.associate(state_id, "authentication_level", to_save);
} else if(request_type == "access_token") { // Request to /token
  // Retrieve the value
  var lvl = OAuthMappingExtUtils.getAssociation(state_id, "authentication_level");
  // Return it in the bearer token
  stsuu.addContextAttribute(new com.tivoli.am.fim.trustserver.sts.uuser.Attribute("AUTHENTICATION_LEVEL" ,"urn:ibm:names:ITFIM:oauth:response:attribute", lvl));

}

Or, if you need the credential attribute in the pre-token rule of an authorization code flow, often needed when populating an id_token, use this snippet in the pre-token rule:

if (request_type == "access_token" && grant_type == "authorization_code") { // Request to /token, when performing the authorization code flow

  // We need a token value to lookup, get the token string. We know that it MUST be posted in the authorization code flow
  var code = stsuu.getContextAttributes().getAttributeValueByNameAndType("code", "urn:ibm:names:ITFIM:oauth:body:param");
  // Use the code string to get a token object - which contains a state_id
  var token = OAuthMappingExtUtils.getToken(code);
  // Double check we got a token successfully.
  if(token != null) {  
    // Read the state_id
    var state_id = token.getStateId();
    // Use the state_id to retrieve the value. 
    var groups = OAuthMappingExtUtils.getAssociation(state_id,"AUTHENTICATION_LEVEL");

Sharing state isn’t limited to sharing between /authorize and /token. The same associated attribute can be used in requests to /userinfo, /introspect and /session.

Click here to rate this article

Rate this article :

Software Engineer - IBM Security Access Manager

More Access and Authentication stories
By Carsten Hagemann on June 20, 2022

Getting started with the IBM Verify SDK

The IBM Verify SDK is a library available for Android and iOS and provide classes to create rich native client mobile applications that interact with IBM Security Verify and IBM Security Verify Access, so that enterprises can easily integrate flexible and intelligent multi-factor authentication into their applications. Multi-factor authentiation (MFA) verifies an indiviual’s identity by […]

Continue reading

By Jeroen Tiggelman on August 4, 2019

IBM Security zSecure Suite 2.4 announced

IBM Security zSecure suite V2.4 was announced on July 23, 2019 with a planned availability date of September 30, 2019. You can read the US announcement letter here. RACF has made new JSON Web Token functionality in support of Multi-Factor Authentication also available for z/OS V2.2 and V2.3. Details about zSecure compatibility fixes can be […]

Continue reading

By Gerard Boekhoud on July 24, 2019

IF001 for IGI 5.2.5.1 now available

On July 19, 2019 we made  IF001 on top of IGI 5.2.5.1 publicly available on FixCentral. This iFix include some strong performance improvements especially within the Access Certification module. Improvements are made in the following areas: a. Time to launch Campaign Summary Page (Especially in the event of a high number of campaigns). This improves by […]

Continue reading