Recipe: Multi-Tenant Setup
Build a multi-tenant SaaS where each of your customers connects their own services through BlueNexus, with your branding.
What You'll Build
An application where:
- Your customers sign up on your platform
- They connect their services (Google, Slack, etc.) through a white-labeled BlueNexus flow
- Your app accesses their connected services via MCP or REST API
- Each customer only sees and accesses their own data
Architecture
Your SaaS App
│
├── Customer A authenticates via OAuth ──→ BlueNexus (white-label)
│ └── Customer A connects Google, Slack
│
├── Customer B authenticates via OAuth ──→ BlueNexus (white-label)
│ └── Customer B connects Jira, Notion
│
└── Your backend calls API with each customer's token
├── Customer A's token → accesses only A's Google, Slack
└── Customer B's token → accesses only B's Jira, Notion
Step 1: Create a White-Label Auth Client
curl -X POST https://api.bluenexus.ai/api/v1/auth-clients \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "YourApp",
"type": "confidential",
"roles": ["whitelabel-customer"],
"domains": ["yourapp.com"],
"redirectUris": [
"https://yourapp.com/auth/callback",
"https://yourapp.com/connections/callback"
],
"allowedScopes": [
"universal-mcp-read-write",
"agents-all",
"llm-all",
"connections",
"account",
"openid",
"profile",
"email"
],
"branding": {
"logo": "data:image/png;base64,YOUR_LOGO",
"description": "YourApp — AI-powered workspace",
"brandColor": "#4F46E5"
}
}'
Save the clientId and clientSecret.
Step 2: Implement OAuth in Your App
When a customer signs up or connects their BlueNexus account:
// Generate PKCE values
const codeVerifier = crypto.randomBytes(64).toString("base64url");
const codeChallenge = crypto
.createHash("sha256")
.update(codeVerifier)
.digest("base64url");
// Store codeVerifier in session
session.codeVerifier = codeVerifier;
session.state = crypto.randomUUID();
// Redirect to BlueNexus (shows YOUR branding)
const url = new URL("https://app.bluenexus.ai/oauth/authorize");
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", CLIENT_ID);
url.searchParams.set("redirect_uri", "https://yourapp.com/auth/callback");
url.searchParams.set("scope", "universal-mcp-read-write connections account openid");
url.searchParams.set("state", session.state);
url.searchParams.set("code_challenge", codeChallenge);
url.searchParams.set("code_challenge_method", "S256");
res.redirect(url.toString());
Step 3: Handle the Callback
app.get("/auth/callback", async (req, res) => {
const { code, state } = req.query;
// Verify state
if (state !== session.state) throw new Error("CSRF mismatch");
// Exchange code for tokens
const tokenRes = await fetch("https://api.bluenexus.ai/api/v1/auth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: "https://yourapp.com/auth/callback",
code_verifier: session.codeVerifier,
}),
});
const { access_token, refresh_token } = await tokenRes.json();
// Store tokens for this customer (encrypted!)
await db.customers.update(customerId, {
bluenexusAccessToken: encrypt(access_token),
bluenexusRefreshToken: encrypt(refresh_token),
});
});
Step 4: Let Customers Connect Services
Prompt customers to connect their services:
async function connectService(customerId, providerId) {
const token = await getCustomerToken(customerId);
const res = await fetch(
"https://api.bluenexus.ai/api/v1/connections/initiate",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
providerId,
redirectUrl: "https://yourapp.com/connections/success",
}),
}
);
const { authorizationUrl } = await res.json();
// Redirect customer to authorizationUrl
}
Step 5: Access Customer Data via MCP
When your app needs to interact with a customer's services:
async function queryCustomerServices(customerId, prompt) {
const token = await getCustomerToken(customerId);
const res = await fetch("https://api.bluenexus.ai/mcp", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"X-Response-Format": "json",
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "tools/call",
params: {
name: "use-agent",
arguments: { prompt },
},
id: "1",
}),
});
return res.json();
}
// Each customer's token only accesses THEIR data
await queryCustomerServices("customer-A", "What's on my calendar today?");
await queryCustomerServices("customer-B", "Show my Jira backlog");
Key Points
- Tenant isolation is built in — each customer has their own BlueNexus account with their own connections. A customer's token can only access their data.
- White-label branding — customers see your brand, not BlueNexus, throughout the OAuth and connection management flows.
- Token management — store and refresh tokens for each customer. Use the refresh token grant when access tokens expire.
- Credit billing — set
billingAccountType: "owner"on agents if you want to absorb credit costs, or"user"to charge per-customer.