Connect Two Auth0 Tenants
Last Updated:
Overview
This article explains how to connect two Auth0 tenants to the same application. This configuration is often required when user data must be stored in different regions, necessitating the creation of separate tenants.
Applies To
-
Auth0 Tenants
-
Enterprise Connections
-
Custom Databases
Solution
Two options exist for connecting Auth0 tenants.
-
Enterprise Connection The recommended option is to configure an enterprise connection between both tenants, such as an OpenID Connect (OIDC) connection.
-
Custom Database If an enterprise connection is not feasible, for instance, when a single username and password login option is required, a custom database can be created on Tenant A. Use the Resource Owner Password Grant (ROPG) to connect the custom database to Tenant B.
Custom Database login script for Tenant A:
async function login(email, password, callback) { const axios = require("axios"); const jwt_decode = require("jwt-decode"); const querystring = require("querystring"); const tokenEndpoint = "https://<TENANT_B>/oauth/token"; // Tenant B token endpoint const clientId = "<TENANT_B_CLIENT_ID>"; // Tenant B clientID async function passwordGrant() { //Password grant request to tenant B const res = await axios.post( tokenEndpoint, querystring.stringify({ grant_type: "password", client_id: clientId, username: email, password: password, scope: "openid profile email", }), { headers: { "Content-Type": "application/x-www-form-urlencoded", }, } ); return res.data; } function decodeToken(token) { const decodedToken = jwt_decode(token); const user = { user_id: decodedToken.sub, email: decodedToken.email, username: decodedToken.name, name: decodedToken.name, email_verified: decodedToken.email_verified, picture: decodedToken.picture, nickname: decodedToken.nickname, }; return user; } try { const authResponse = await passwordGrant(); var user = decodeToken(authResponse.id_token); return callback(null, user); } catch (err) { return callback( new WrongUsernameOrPasswordError(email, "Wrong username or password :(") ); } }
Custom Database get user script for Tenant A:
async function getByEmail(email, callback) {
const axios = require("axios");
const querystring = require("querystring");
const jwt_decode = require("jwt-decode");
const tokenEndpoint = "https://<TENANT_B>/oauth/token"; // Tenant B token endpoint
const getUserEndpoint =
"https://<TENANT_B>/api/v2/users-by-email?email=" + email;
const API2clientId = "<TENANT_B_CLIENT_ID>";
const API2clientSecret = "<TENANT_B_CLIENT_SECRET>";
const aud = "https://<TENANT_B>/api/v2/";
async function getAccesToken() {
//Password grant request to tenant B
const res = await axios.post(
tokenEndpoint,
querystring.stringify({
grant_type: "client_credentials",
client_id: API2clientId,
client_secret: API2clientSecret,
audience: aud,
}),
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
return res.data.access_token;
}
async function getToken() {
let token = "";
if (global.externalDBToken) {
token = global.externalDBToken;
const decodedToken = jwt_decode(token);
var dateNow = new Date();
if (decodedToken.exp < dateNow.getTime() / 1000) {
token = await getAccesToken();
global.externalDBToken = token;
}
} else {
token = await getAccesToken();
global.externalDBToken = token;
}
return token;
}
async function getUser() {
const token = await getToken();
const res = await axios({
method: "GET",
url: getUserEndpoint,
headers: {
Authorization: `Bearer ${token}`,
},
});
return res.data;
}
function decodeToken(user) {
const newUser = {
user_id: user.user_id,
email: user.email,
name: user.name,
email_verified: user.email_verified,
nickname: user.nickname,
picture: user.picture,
};
return newUser;
}
try {
const authResponse = await getUser();
if (authResponse.length === 0) {
return callback(null);
}
const user = decodeToken(authResponse[0]);
return callback(null, user);
} catch (err) {
return callback(new Error("Could not determine if user exists or not :("));
}
}