Multi-Tenancy in Spring Security
https://diginomica.com/2015/12/08/does-multi-tenancy-really-matter-anymore/

Multi-Tenancy in Spring Security

Nowadays people are adapting "As a Service" architecture. Which includes Saas, Paas, Iaas and DBaas etc. Most of the people wondering why people are following above architecture. What is the benefit of using that.

SAAS Advantage

SAAS can be either Multi or Single Tenancy based.

Multi/Single Tenancy SAAS

Accoring to Wikipedia :

The term "software multitenancy" refers to a software architecture in 
which a single instance of software runs on a server and serves multiple 
tenants. A tenant is a group of users who share a common access with 
specific privileges to the software instance.

In a single-tenancy architecture, the tenant purchases their own copy 
of the software and the software can be customized to meet the specific 
and needs of that customer.

Single tenancy means specific software and all of the supporting infrastructure serves a single type of customer. With single tenancy, each type of customer has their own independent database and instance of the software. With this option, there’s essentially no sharing going on. Everyone has their own, separate from everyone else.

For Example you want to create an application which is taking hotel booking. By default you will be either opting for Monolithic or Micro Service approach and you will end up creating single tenant application which means it will only work for users who register on this application.

What if you want to create an application where different hotel can come and provide all configuration details and get hotel website up and running ?

Single Tenant

  • In this case if your application is single tenant you will go through same steps again which includes database set up , infrastructure etc. Now how to avoid all this because whenever you want to create new application you will end up doing exact same thing.
  • This is happening because you opted for single tenancy where your application is supposed to support only one type of customers.

Multi Tenant

  • Multi tenancy can play an important role in this to avoid all above duplicate tasks and can boost your productivity and revenue. People also use this a lot to build SAAS ( software as a service).
  • To handle multi tenancy we have to introduce an application Id or Tenant Id which will be associated to one Client. When any user performs any operation on that client we will associated his activity with application Id.
  • If you create new features they will be easily available to other clients as well.
  • Any number of the Clients can visit your application where they have to provide all details related to hotels and we don't have to worry about anything.
  • UI which is customer facing can be common or can be different but backend will be same because every time when you get request from Frontend you will get client Id and you can send data related to that ClientId.

When we think of any Microservices Spring Boot is better choice (Java). For Security we use Spring Security and we can also use integrate OAuth 2.0 for token based Authentication and Authorization.

For multi tenancy tenant id should be present in token which is issued to any customer when he logs in system.

A field called appId or tenant id can be added in payload part of access token.

{
	"user_name": "johnwick",
	"scope": ["openid"],
	"appId": "ABC",
	"exp": 1551013392,
	"authorities": ["ROLE_AUTHENTICATED_USER"],
	"jti": "70c06470-8c05-4252-bf62-156dad77af24",
	"client_id": "lolosas"
}

I am assuming you already have Spring Security and Oauth integration in place. I did few configuration to achieve multi tenancy in application.

  1. For multi tenant application you have to pass Application Id in header or if you are using different subdomains for different tenants you have to map application Id.If you are not able to fetch application id you have to throw an error.
  2. Adding application id in JWT token.
  3. Once user is authenticated we can fetch application id from JWT token.
  4. There is class called JwtAccessTokenConverter in Spring Security which transforms JWT to OAuth Authentication and vice versa.
  5. Once user is logged in generally we return either PreAuthenticated or UsernamePassword authenticationToken. We need to extend any of the class as per our requirement and add field application Id.
public class TenantAwareOAuth2Request extends OAuth2Request {



	private static final long serialVersionUID = -2661220713875278012L;



	private String tenant;



	public TenantAwareOAuth2Request(OAuth2Request oAuth2Request) {

		super(oAuth2Request);

	}



	public String getTenant() {

		return tenant;

	}



	public void setTenant(String tenant) {

		this.tenant = tenant;

	}



}


Once Authenticated we have to return above class object from Authentication provider.

new TenantPreAuthenticatedAuthenticationToken(userDetails.getUsername(),

							userDetails.getPassword(), userDetails.getAuthorities(),

							((LinkedHashMap<String, String>) authentication.getDetails())

									.get(SecurityConstants.APPLICATION_NAME));

We have to send Application Id to Authentication provider by using Custom Tokens.

public Authentication authenticate(Authentication authentication)

Suppose you also have OTP based Authentication in that case you have to use

public class OTPAuthenticationToken extends TenantAuthenticationToken {



	private static final long serialVersionUID = 1L;



	public OTPAuthenticationToken(Object principal, Object credentials) {

		super(principal, credentials);

	}



	public OTPAuthenticationToken(String username, String password, String appId) {

		super(username, password, appId);

	}


}

Above process is to authenticate a customer against specific Client / application.

6. Once above process is done. Application Id will appear in OAuth2Authentication class. To add application id in Access Token we have to extend JwtAccessTokenConverter which contains method to add fields in access token.

    @Override

	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {

		DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);

		if (authentication.getUserAuthentication() instanceof TenantPreAuthenticatedAuthenticationToken) {

			customAccessToken.getAdditionalInformation().put(SecurityConstants.APPLICATION_NAME,

					((TenantPreAuthenticatedAuthenticationToken) authentication.getUserAuthentication()).getAppId());

		}

		return super.enhance(customAccessToken, authentication);

	}


7. Once above steps are done. Access token which will be generated will contain app id.


As you can see when Authentication object is constructed from access token "app id" is not present so to fetch app id from access token in application either you can use third party APIs or you can extend OAuth2Request and add app id field in that. So when Authentication object will be constructed using Access token we can fetch app id from there and set into extended request class.

public class TenantAwareOAuth2Request extends OAuth2Request {


	private static final long serialVersionUID = -2661220713875278012L;


	private String appId;


	public TenantAwareOAuth2Request(OAuth2Request oAuth2Request) {

		super(oAuth2Request);

	}


	public String getAppId() {

		return appId;

	}


	public void setAppId(String appId) {

		this.appId = appId;

	}

}

To set extended version of Request class in OAuth2Authentication we need to extend DefaultAccessTokenConverter and provide below implementation. We also have to set extended DefaultAccessTokenConverter to JWTAccessTokenConvertor.

converter.setAccessTokenConverter({Extended DefaultAccessTokenConverter class object});


public OAuth2Authentication extractAuthentication(Map<String, ?> map) {

		OAuth2Authentication authentication = super.extractAuthentication(map);

		TenantAwareOAuth2Request tenantAwareOAuth2Request = new TenantAwareOAuth2Request(

				authentication.getOAuth2Request());

		tenantAwareOAuth2Request.setAppId((String) map.get(SecurityConstants.APPLICATION_NAME));

		return new OAuth2Authentication(tenantAwareOAuth2Request, authentication.getUserAuthentication());

	}

Authentication object fields.

So whenever user will login in any application post authentication app id will be added in Authentication which can be used to fetch only data which belongs to that user in that application.

Using above approach single user can login in different applications and all data can be differentiated with the help of application id.

Let me know if you have any simpler approach in mind.

Happy Coding...!!!

To view or add a comment, sign in

More articles by Raghunandan Gupta

Others also viewed

Explore content categories