Extending Spring Security OAuth for Multi-Tenant

In being a SaaS company, we are gradually chipping away at our good old monolith, turning pieces into micro-services that can scale horizontally, and that scale efficiently by use of multi-tenancy. A single micro-service can have multiple instances, and each instance serves a multitude of customers. Multi-tenancy has implications on application state, and a common pattern is for database tables to be shared across tenants, where each record links to a specific tenant. It also requires some lifting to make sure that the application always understands which tenant it is working for.

We are a Spring shop, and happy users of Spring Boot for our micro-services. We recently built the “Jama OAuth service”, which is an OAuth 2 compatible authorization server, that essentially issues access tokens to clients of our system (given their credentials). It implements OAuth’s so-called “client credentials” flow/grant type.

Jama OAuth service issues access tokens to clients of our system

The access tokens are used to protect some REST resources. It was a must have requirement that the Jama OAuth service would support multi-tenancy. It was a natural choice to look at Spring Security, specifically Spring Security OAuth. This library however, does not, out of the box, support multi-tenancy.

When issuing access tokens, it is an interesting option to use JWT tokens. As this link shows, JWT tokens are not just a unique identifier by which the authorization server can verify your claim, rather they do include the details of the claim. In our case we could include not only information about the client, but also about what tenant they belong to. Our tokens could look something like below, which is awesome, because it would mean that resources in our ecosystem can validate an access token (extract all the information that they need) without having to contact the Jama OAuth service, an enormous performance gain.

1

2

3

4

5

6

7

8

9

{

"exp":12345,

"scope":[

"read"

],

"tenant":"tenant1234",

"jti":"d08e06d9-7408-4a01-bcf0-409ad23391ce",

"client_id":"test1234"

}

This is almost exactly a JWT token that Spring Security OAuth spits out, using its JwtAccessTokenConverter, except for the custom property “tenant“. Unfortunately, there is no clear extension point to add custom properties. Conversely, when using Spring Security to validate an access token, what it gives your application code access to is an OAuth2Request, that does not include any custom properties. There is one more piece of tenant-awareness; in all we needed to address the following:

Make sure that the application understands which is the right tenant when an access token is requested. This one is simple for us: we have standardized on including an HTTP request header that identifies the tenant. If you forget to include this header, you are awarded an error message. If you include this header, we can use it together with your provided user name and password, to authenticate your request. We would then proceed to return you an access token (JWT token), see the first list item here.

Add custom property “tenant” to JWT tokens.

Read custom property “tenant” from JWT tokens and make it available to our application code.

Add Custom Property

Creating tokens is a function of the authentication server (in our case the “Jama OAuth service”). JWT tokens are generated in Spring by the JwtAccessTokenConverter. So, of course we override that class to get our way. It is being configured in our Spring JavaConfig as follows:

When retrieving the details of our client we take the client ID given by Spring, and combine it with the tenant header from the request (that we require users to include when offering their client credentials).

returngetClientEntityFromDatabase(clientId,tenant);// this includes some assertions to make sure the requested client exists

}

Read Custom Property

This assumes that your resource server is also using Spring Security. It may or may not be the same component as your authentication server (in our case the “Jama OAuth service”). JWT tokens are processed in Spring by a small army of classes, but we chose to override theDefaultAccessTokenConverter. This needs to be injected into the JwtAccessTokenConverter, here of course our own TenantAwareJwtAccessTokenConverter. It is being configured in our Spring JavaConfig as follows:

Inside that class we can get access to a map that contains the raw data extracted from the JWT token, before Spring throws out our custom properties. Note that the super implementation already returns an OAuth2Authentication object. Inside that object, we substitute the originalOAuth2Request with our custom TenantAwareOAuth2Request.

If your resource server is not using Spring Security, there is other libraries to read JWT tokens, and to read the tenant off of these tokens. We have successfully used JJWT for that.

Conclusion

While Spring Security does not make it very easy to add your own properties to JWT tokens, it can certainly be done in an acceptable manner. Having tenant information available in JWT tokens makes these tokens “fully qualified” in a multi-tenant environment, and thus usable without needing additional (tenant) information to be retrieved, when given an access token. This makes it also possible to do multi-tenant-enabled authentication, even on resource servers that aren’t the same component as your authentication server.

You can see how this approach would work for additional properties, on top of just the “tenant” custom property. In fact, make sure that the JWT token contains just enough information so that resource servers can authorize the client without contacting the authorization server.

The Jama Product Development Platform is a solution for complex systems development.