Articles

OAuth: SAML and JWT as a Grant Type

Share this post:

OAuth: SAML and JWT as a Grant Type

In an earlier article it was demonstrated how Security Access Manager supports RFC 7523 using JWT as a method for OAuth clients to make requests to OAuth endpoints which require authentication such as /token and /introspect. However there is another portion to this RFC which goes into detail on how an OAuth client may present a JWT as security assertion which represents the identity of the resource owner. There is also a sibling RFC which goes into detail on how the same can be performed with a SAML assertion.

Security Access Manager has supported these grant types since version 9.0.2.0.

The Assertion Grant Type

The assertion grant type is defined by RFC 7521 and identifies method for an OAuth client to authenticate or present a resource owner assertion as a method by which oauth tokens can be granted. When using security assertions as a grant type the identify the assertion parameter:

assertion
      REQUIRED.  The assertion being used as an authorization grant.
      Specific serialization of the assertion is defined by profile
      documents.

The RFC explicity identifies SAML and JWT as specific assertion types for consideration, which are each captured in RFCs 7522  and 7523. These both define a value for the grant_type parameter as urn:ietf:params:oauth:grant-type:jwt-bearer and urn:ietf:params:oauth:grant-type:saml2-bearer.

Assertion Bearer Grant Types on Security Access Manager

Security Access Manager supports both the SAML and JWT assertion grants via the same methodology as the Resource owner password credentials flow, which has the Authorization Server verify the provided credentials or in this case assertion in the pre-token mapping rule.

The pre-token rule is responsible for taking assertion and verifying it by any available means whether there is logic present in the pre-token rule to do so natively or via a callout to another service such as an STS. Calling an STS is a logical method, and using the LocalSTSClient described in earlier articles is an efficient means to validate the assertion using the local STS runtime.

JWTGrant

 

In order to configure the JWT bearer grant 3 steps need to be followed:

  1. Create an API Protection Definition with the JWT bearer grants enabled
  2. Create the validation STS chain(s)
  3. Update the pre-token mapping rule.

 

Configuring the API Protection Definition

To perform the assertion bearer grants, you need to create a definition with one or both of the grant types enabled

 

Creating the STS validation chain

The video below shows configuring the  STS with JWT(validate) -> STSUU (issue). The applies to uses the grant_type value  urn:ietf:params:oauth:grant-type:jwt-bearer  and any issuer.

Mapping Rule Updates

The default mapping rule already contains logic to demonstraite handling the JWT bearer grant, but doesn’t include the callout to the STS.
The OOTB logic is:

var enableAssertionGrants = false;
if (enableAssertionGrants) {

	// The grant type
	var assertion = null
	var temp_attr = stsuu.getContextAttributes().getAttributeValuesByNameAndType("assertion", "urn:ibm:names:ITFIM:oauth:body:param");
	if (temp_attr != null && temp_attr.length > 0) {
		assertion = temp_attr[0];
	}

	if (grant_type != null && (grant_type == "urn:ietf:params:oauth:grant-type:jwt-bearer"  || grant_type == "urn:ietf:params:oauth:grant-type:saml2-bearer")) {

		// Implement Assertion validation here. For example, invoke the
		// STS using the STSClientHelper. See the Javadoc for more information.
		var assertionValid = false;

		if (!assertionValid) {
			OAuthMappingExtUtils.throwSTSUserMessageException("Invalid Assertion. Authentication failed.");
		}

		// Someone may have provided a username in this request as a post parameter, remove it!
		stsuu.getContextAttributes().removeAttributes("username", null);

		// Use the subject of the assertion as the username
		var subject = null;

		if (assertionValid) {
			// set the username	
			stsuu.addContextAttribute(new com.tivoli.am.fim.trustserver.sts.uuser.Attribute("username", "urn:ibm:names:ITFIM:oauth:rule:decision", subject));
		}
	}
}

This provides an initial outline of the logic needed, but doesn’t make any assumptions about how the assertion can be verified. Adding the LocalSTSClient call is as follows:

importClass(Packages.com.tivoli.am.fim.fedmgr2.trust.util.LocalSTSClient);
var enableAssertionGrants = true;
if (enableAssertionGrants) {


  var grant_type = null;
  // The grant type
  var temp_attr = stsuu.getContextAttributes().getAttributeValuesByNameAndType("grant_type", "urn:ibm:names:ITFIM:oauth:body:param");
  if (temp_attr != null && temp_attr.length > 0) {
    grant_type = temp_attr[0];
  }

  var client_id = null;
  // The client ID
  temp_attr = stsuu.getContextAttributes().getAttributeValuesByNameAndType("client_id", "urn:ibm:names:ITFIM:oauth:body:param");
  if (temp_attr != null && temp_attr.length > 0) {
    client_id = temp_attr[0];
  }

  // The presence of an assertion is validated by the authorization server engine prior to rule execution
  var assertion = null
  temp_attr = stsuu.getContextAttributes().getAttributeValuesByNameAndType("assertion", "urn:ibm:names:ITFIM:oauth:body:param");
  if (temp_attr != null && temp_attr.length > 0) {
    assertion = temp_attr[0];
  }

  if (grant_type != null && grant_type == "urn:ietf:params:oauth:grant-type:jwt-bearer") {

    // Parse a base token. In this case a BinarySecurityToken containing a JWT.
    var base_token = IDMappingExtUtils.stringToXMLElement('' + assertion + '');

    var res = LocalSTSClient.doRequest("http://schemas.xmlsoap.org/ws/2005/02/trust/Validate", grant_type, client_id, base_token, null)
    var assertionValid = (res.errorMessage == null);
    if (!assertionValid) {
      OAuthMappingExtUtils.throwSTSUserMessageException("Invalid Assertion. Authentication failed [" + res.errorMessage + "].");
    }

    stsuu.getContextAttributes().removeAttributes("username", null);

    var assertion_stsuu = new STSUniversalUser();
    assertion_stsuu.fromXML(res.token);
    var subject = assertion_stsuu.getAttributeValueByName("sub");

    if (assertionValid) {
      stsuu.addContextAttribute(new com.tivoli.am.fim.trustserver.sts.uuser.Attribute("username", "urn:ibm:names:ITFIM:oauth:rule:decision", subject));
    }
  }
}

In this snippet there are a few things to note:

  • Additional claims can be retrieved from the assertion_stsuu and added to the grant.
  • The client_id is used as the issuer, so if a custom chain for a given client_id is needed, just create a new chain with a specific issuer address value.
  • The default pre-token rule contains logic to pull out the grant type, it was included here for completeness.

 

The Grant at Runtime

At runtime there is one request to be made, the client does a HTTP POST with their credentials and the assertion to the token endpoint.

 

Debugging

 

This trace string can be used to debug any issues encountered in the flow:

com.tivoli.am.fim.oauth20.*=ALL:com.tivoli.am.fim.trustserver.sts.utilities.*=ALL:com.tivoli.am.fim.trustserver.sts.modules.*=ALL

Afterword

Is there something you’d like to see written up? Feel free to contact me with your suggestions and requests.

Click here to rate this article

Rate this article :

Software Engineer - IBM Security Access Manager

More Articles 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 Martin Schmidt on July 11, 2019

Modernizing your B2C Portal Security – LDAP Proxy Deep Dive

In this part of our series we are taking a deeper look on how the LDAP reverse proxy works and what is needed to be done to make it work. Enable CI In this part we look at what needs to be done on the CI side and what information needs to be collected. We […]

Continue reading

By Martin Schmidt on May 4, 2019

Modernizing your B2C Portal Security – Desired End State

Proposition: As we have seen in part one of this series, managing customer identities for a portal can be a challenge and distraction for the business.  In this part of the series we will outline how a modernized solution for a portal security can simplify operations and free your team up to focus on the […]

Continue reading