Using RegEx-Defined Scopes With OpenAM

Summary

Using RegEx-Defined Scopes With OpenAM

In many shopping scenarios, and especially in the PSDII world, you need to express consent in a form much like this: I consent to let Amazon withdraw amount X from (from my bank account) in order to purchase product Y. While this perfectly aligns with the ideas of OAuth 2.0 protocol, there are some pitfalls.

Most OAuth 2.0 implementations use a static list of fixed scopes to express something like: Do you consent to give READ access (to some resource)?

Here READ is a scope that can be mail, twitter feed, and others, in different implementations. With OAuth 2.0’s growing popularity and adoption in frameworks like OpenBanking or BerlinGroup’s NextGen PSD2, this fixed list of scopes needs to be enhanced to express consent and authorization decisions of the form. For example, I consent, that can withdraw $965.00 from my bank account in order to pay for guitar G (or any other product you would like to purchase).

Here, scopes have the form, which is a reference to the purchase. Note that in the OpenBanking and NextGenPSDII worlds, the text behind the colon is the PaymentID. Also note that OpenBanking uses the OIDC RequestParameter to express this consent.

Using WITHDRAW:965#Guitar

The only fixed element here is the withdraw action. Everything after the colon is variable and depends on the type of purchase you make. There’s nothing in the OAuth 2.0 specs that would restrict the set of possible scopes an OAuth 2.0 client can ask for to be defined by a regular expression. In my discussions with OAuth2 experts, I learned that there are some different, hidden assumptions about the definition of scopes. Are they finite? Are they defined with help of non-terminating characters? Does the authorization server (AS) need to parse them? Long story short, for our purposes, we we need to use variable scopes, which are sometimes called dynamic scopes.

Implementing OpenAM

OpenAM assumes a fixed list of scopes that are available for each OAuth 2.0 client, where each scope consists of terminating characters only. The OpenAMScopeValidator class efficiently checks what the client is allowed to ask for if a client only asks for scopes:

In order to allow for scopes defined by regular expression like ^WITHDRAW:., which matches our example above, you just need to amend the OOTB implementation to do a bit of pattern matching:

@Override

public Set<String> validateAccessTokenScope(ClientRegistration clientRegistration, Set<String> scope,

OAuth2Request request) {

List<String> requestedScopes = new ArrayList(scope);List<String> authorizedScopes = new ArrayList();List<String> allowedScopes = new ArrayList(clientRegistration.getAllowedScopes());

if (scope == null || scope.isEmpty()) {
   return clientRegistration.getDefaultScopes();
}

Iterator it = allowedScopes.iterator();
while (it.hasNext()) {        
authorizedScopes.addAll(filter(requestedScopes, (String)it.next()));

   }

   return new HashSet<>(authorizedScopes);
}

The magic is in the filter method:

private List<String> filter(List<String> reqestedScopes, String regex) {

   Pattern pattern = Pattern.compile(regex); //move outside method in production
   List<String> matching = reqestedScopes.stream().filter(pattern.asPredicate()).collect(Collectors.toList());
   return matching;
}

Include that piece of code in your custom scope validator class (you’ll find sample code including Maven build files in the OpenAM Samples directory).

After that, change the scope validator class to your new class:

Now you can configure your OAuth2 agents to use ReGex-defined scopes:

Let’s give it a try with Postman:

And verify the opaque token at the tokeninfo endpoint:

We get all the scopes that are permitted for this client, including the ones that start with the letter a.

Summary

OpenAM can be easily modified to cater the scope implementation that’s best for your use case. Note that different implementations can have different impacts on performance, and other things. Thus, if you only have a classic list of fixed values, stick with the OOTB implementation. However, if you need to cater to variable scopes (defined by regular expressions), use your custom class.

This allows OpenAM to be used in scenarios like NextGenPSDII (defined by Berlin Group) and many other modern OAuth2 use cases.