Search code examples
amazon-web-servicesamazon-s3amazon-cloudfrontmulti-tenant

Serve SPA multi-tenants through AWS CloudFront from a single S3 bucket


Given the following considerations:

  • Single S3 bucket containing static Frontend SPA files.
  • Frontend is being served through CloudFront, where each tenant has their own CloudFront distribution (tenantA.domain.com, ... tenantZ.domain.com).
  • Each tenant has their own configurations (which can be fetched from a Configuration service resolving the domain).
  • Each CloudFront needs to inject such configurations in the Frontend at run time.

Consider the diagram below.


I am thinking of a Lambda function, that queries the "Configuration service" (possibly caching the response), and then setting globally scoped variables (e.g. window.config1) for the SPA to use. Is such a scenario possible through CloudFront? Is there a more common/standardized way?


Solution

  • Yes you can do this, I have done it! (Been meaning to write a blog about it. I work on a multi-tenant SAAS platform, where tenants have their own identity provider configuration for Cognito. What I did was I had a file config.js, looks something like this which had a bunch of Cognito settings: (I'm using ReactJS)

    window.TENANT_CONFIG = {
          REACT_APP_USERPOOL_REDIRECT_SIGNIN : "https://todo.somesite.com/",
          REACT_APP_USERPOOL_REDIRECT_SIGNOUT : "https://todo.somesite.com/",
          REACT_APP_WEB_CLIENT_ID : "todo",
          REACT_APP_TENANT_NAME : "todo" ....
        };
    
    

    This tile is included as a javascript script at the top of my index.html

    <script type="text/javascript" src="%PUBLIC_URL%/config.js"></script> 
    

    Lambda

    You need a Lambda function in us-east-1 which will become a Lambda at Edge in your Cloudfront distribution. This function must be only 128 MB and have no environment variables. You can refer to this documentation on how to create the Lambda function. But in general you need to construct the window.TEN.... You can read the event.Records[0].cf.request.headers['host'][0].value to determine wether your call came from one domain or the other, and construct the appropriate config.js file. My Lambda function actually reads configuration from an S3 bucket. Yours will be different.

    CloudFront

    If you can generate the correct certificate, you only need one CloudFront domain actually. Just make sure you have added the CNames of all the different subdomains. Then you need to add a behaviour with a path pattern of: config.js. Configure a Lambda Function Associations of Viewer Request and Specify your Lambda Function ARN. And you are good to go!