auth0/auth0-multitenant-spa-api-sample

Name: auth0-multitenant-spa-api-sample

Owner: Auth0

Description: JQuery SPA + Node.js API with multi-tenant support

Created: 2015-11-09 17:44:35.0

Updated: 2018-05-16 04:58:31.0

Pushed: 2017-12-22 02:33:20.0

Homepage: null

Size: 614

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Auth0 Multitenant App sample

This sample shows how to implement a multi-tenant scenario, where:

You can read more about multi-tenant scenarios here: https://auth0.com/docs/saas-apps.

The API is based on this sample: https://github.com/auth0/multitenant-jwt-auth.

Features

The SPA is able to:

The API is able to:

How it works
SPA Client

The client is a node.js express app. The only code executing server side is the one for:

tion getTenantFromDomain(host){
r re = new RegExp(/(\w+)-yourcompany.com$/);
r matches = host.match(re);
 (matches && matches.length > 1){
return matches[1];

turn null;


get('/', function(req, res, next) {
r tenantName = getTenantFromDomain(req.hostname);
 (!tenantName) return next('Invalid domain: ' + req.hostname);

r tenantConfig = _.find(tenants,'name',tenantName);
 (!tenantConfig) return next('Invalid Tenant ' + tenantName);
r loginConfig = {
auth0Domain:tenantConfig.auth0Domain,
auth0ClientId:tenantConfig.auth0ClientId

s.render('index', loginConfig);

Once the initial page is rendered, the app continues as a JQuery Single Page App. It uses the auth0Domain and auth0ClientId obtained from server side to configure auth0.js, which will be used to perform the Login.

The access_token obtained during login is saved to localStorage and will be used later to invoke the API. It is also used to fetch the user's profile.

API

The custom API uses express-jwt for validating JWTs.

expressJwt = require('express-jwt');
express = require('express');
app = express();

use('/api', expressJwt({
cret: secretCallback,
gorithms: [ 'HS256','RS256'] //the 2 algorithms supported by Auth0

Because of the multi-tenant support, the secret used to sign tokens is not static and hence a secretCallback is used. The secretCallback supports verifying tokens signed with HS256 algorithm (clientId) and RS256 (asymmetric keys). It uses an LRU cache to store the secrets.

tion secretCallback (req, header, payload, cb){
var cacheKey = payload.iss + '|' + payload.aud ;
r cachedSecret = secretsCache.get(cacheKey);
 (cachedSecret) { 
return cb(null, cachedSecret);


r tenant = data.getTenantByIssuer(payload.iss);
 (!tenant) { 
return cb(new Error('Invalid issuer '+payload.iss)); 


itch (header.alg) {
case 'HS256': //client secret
  var secret = new Buffer(tenant.secret, 'base64');
  secretsCache.set(cacheKey, secret);
  return cb(null, secret);
case 'RS256': // asymmetric keys
  var url = payload.iss + '.well-known/jwks.json';
  request.get(url, { json: true, strictSSL: false }, function (err, resp, jwks) {
    if (err) {
      return cb(err);
    }
    if (resp.statusCode !== 200) {
      return cb(new Error('Failed to obtain JWKS from ' + payload.iss));
    }
    var key = _.find(jwks.keys, function(key) {
      return key.kid == header.kid;
    });
    if (!key) {
      return cb(new Error('Failed to obtain signing key used by ' + payload.iss));
    }
    var publicKey = certToPEM(key.x5c[0]);
    secretsCache.set(cacheKey, publicKey);
    return cb(null, publicKey);
  });
  break;
default:
  return cb(new Error('Unsupported JWT algorithm: ' + header.alg));


The API provides a sample secured endpoint for getting a list of users specific for the tenant:

get('/api/users',
nantMiddleware(), //adds tenant's name to req from JWT's issuer claim
nction (req, res, next) {
data.getUsersByTenantIdentifier(req.tenantName, function(err, users){
  if (err) return next(err);
  res.json(users);
});

The tenant middleware adds the tenantName to the request. It obtains the tenant from the tenant store, using the JWT's issuer as a key:

data =require('./data');

le.exports = function(app){
turn function addTenant(req, res, next){
var tenant = data.getTenantByIssuer(req.user.iss);
req.tenantName = tenant.name;
next();


Run the sample locally in a dev environment
1. Modify hosts file

In order to emulate a multi-tenant environment, add the following entries to your machine's hosts file:

0.0.1 tenant1-yourcompany.com
0.0.1 tenant2-yourcompany.com
2. Create the Auth0 accounts

Each tenant needs its own Auth0 account. If the tenant already has an account it can use the existing one. Otherwise you can provision an account for him. You can create new accounts from the Auth0 master account, under the user's menu > New Account option.

You can name each tenant as tenantname-yourcompany.auth0.com.

3. Configure each Auth0 account

For each of the Auth0 accounts:

4. Configure the Tenants Mapping

Create a tenants.js file using tenants-sample.js as a template. For each tenant provide:

Example with tenant1 using its own auth0 domain and using asymetric keys and tenant2 having an auth0 account provisioned by your company and using the client secret:

le.exports = [

name: 'tenant1',
auth0Domain: 'tenant1-custom-domain.auth0.com',
auth0ClientId:'6Y97vVD...C0sxSebDnw7R'


name: 'tenant2',
auth0Domain: 'tenant2-yourcompany.auth0.com',
auth0ClientId:'VIRUMI46...lU8f7d62s4',
secret: 'TUtF1F28Tage...SIMPhdQZuSy'


5. Install and Run the Single Page App
  1. Open a terminal and go to the SPA folder
  2. Install dependencies with npm install
  3. Run the app with npm start
6. Install and Run the API
  1. Open another terminal and go to the API folder
  2. Install dependencies with npm install
  3. Run the app with npm start
7. Use the App!
  1. Navigate to http://tenant1-yourcompany.com:3000, login with tenant1's crentials and call the API. You should see the data associated with tenant1.
  2. Navitage to http://tenant2-yourcompany.com:3000 and do the same for tenant2.
What is Auth0?

Auth0 helps you to:

Create a free Auth0 Account
  1. Go to Auth0 and click Sign Up.
  2. Use Google, GitHub or Microsoft Account to login.
Issue Reporting

If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.

Author

Auth0

License

This project is licensed under the MIT license. See the LICENSE file for more info.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.