How to use email nodes with multiple different credentials?

Hey,

I have a SaaS tool that collected my client’s oauth2 and IMAP data. It is saved secured and crypted in Airtable & Supabase dB.

I want to use IMAP/Gmail nodes to perform automation tasks on their emails (it’s my service), but I can only use my own n8n account credentials.

My question: is there any way to use IMAP & Gmail nodes, using my clients credentials dynamically (maybe using expressions), rather than credentials linked to my n8n account/instance ?

Information on your n8n setup

  • n8n version: pro Cloud
  • Database: Airtable & Supabase
  • Running n8n via n8n cloud

Many thanks for your precious help

You’d most likely have to host a local IMAP api wrapper or use a cloud IMAP api wrapper and then make http requests to those endpoints with the specific cred type or email to be used.

1 Like

Solving Multi-User OAuth in n8n: A Complete Guide to Dynamic Credentials

How to build scalable, secure OAuth credential management for multi-tenant n8n workflows


If you’ve ever tried to build a SaaS application or multi-user automation platform with n8n, you’ve probably hit this wall: how do you handle different users’ credentials dynamically?

n8n’s credential system works great for single-user scenarios, but when you need user-specific API access tokens that refresh automatically, things get complex fast. After building several production systems facing this exact challenge, I’m sharing three battle-tested solutions that actually work.

The Multi-User OAuth Problem

Let’s say you’re building a productivity platform where users can automate their Gmail, Google Drive, and Slack. Each user needs to authenticate with their own accounts, but n8n credentials are static and workflow-scoped. You can’t do this:

// ❌ This doesn't work in n8n credentials
Authorization: Bearer {{ $json.user_token }}

The credential fields are resolved before your workflow execution context exists, so dynamic data from previous nodes isn’t available.

What We Need

  1. User-specific OAuth flows - Each user authorizes your app
  2. Secure token storage - Encrypted, isolated per user
  3. Automatic token refresh - No manual intervention required
  4. n8n integration - Seamless credential fetching in workflows
  5. Multi-provider support - Google, Microsoft, GitHub, Slack, etc.

Solution Architecture

All successful implementations follow this pattern:

User Frontend → OAuth Service → Third-party APIs
      ↓              ↓              ↓
   n8n Workflows ← Token API ← Secure Storage

The key insight: external OAuth service + n8n HTTP requests for credential fetching.


Solution 1: Firebase Auth + Firestore

Perfect for teams already using Google Cloud Platform

Why Firebase?

Firebase gives you OAuth infrastructure, secure storage, and cloud functions out-of-the-box. If you’re already on GCP, this is the most cost-effective and operationally simple solution.

Implementation

1. Set up Firestore collections:

// Collection: userCredentials
{
  "user123_google": {
    userId: "user123",
    provider: "google",
    accessToken: "encrypted_token_here",
    refreshToken: "encrypted_refresh_token",
    expiresAt: "2025-05-25T15:30:00Z",
    scopes: ["gmail.readonly", "drive.readonly"]
  }
}

2. Create Cloud Functions for OAuth:

// Cloud Function: initiateOAuth
exports.initiateOAuth = functions.https.onRequest(async (req, res) => {
  const { userId, provider } = req.query;
  
  // Generate OAuth URL with proper scopes
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: ['https://www.googleapis.com/auth/gmail.readonly'],
    state: `${userId}_${provider}`,
    prompt: 'consent'
  });
  
  res.json({ authUrl });
});

// Cloud Function: oauthCallback  
exports.oauthCallback = functions.https.onRequest(async (req, res) => {
  const { code, state } = req.query;
  const [userId, provider] = state.split('_');
  
  // Exchange code for tokens
  const { tokens } = await oauth2Client.getToken(code);
  
  // Encrypt and store in Firestore
  await db.collection('userCredentials').doc(`${userId}_${provider}`).set({
    userId,
    provider, 
    accessToken: await encrypt(tokens.access_token),
    refreshToken: await encrypt(tokens.refresh_token),
    expiresAt: new Date(tokens.expiry_date)
  });
  
  res.send('✅ OAuth connection successful!');
});

3. API endpoint for n8n:

// Cloud Function: getUserCredentials
exports.getUserCredentials = functions.https.onRequest(async (req, res) => {
  const { userId, provider } = req.params;
  
  let credential = await db.collection('userCredentials')
    .doc(`${userId}_${provider}`).get();
  
  if (!credential.exists) {
    return res.status(404).json({ 
      error: 'No credentials found',
      oauthUrl: `https://your-functions-url/initiateOAuth?userId=${userId}&provider=${provider}`
    });
  }
  
  const data = credential.data();
  
  // Refresh token if needed
  if (data.expiresAt.toDate() < new Date()) {
    await refreshToken(userId, provider, data);
    // Fetch updated data
    credential = await credential.ref.get();
  }
  
  const decryptedToken = await decrypt(data.accessToken);
  
  res.json({
    access_token: decryptedToken,
    expires_at: data.expiresAt.toDate().toISOString()
  });
});

4. n8n integration:

// HTTP Request node in n8n
Method: GET
URL: https://your-region-project.cloudfunctions.net/getUserCredentials/{{ $parameter.user_id }}/google
Headers:
  Authorization: Bearer YOUR_API_KEY

// Use the response in subsequent nodes
Authorization: Bearer {{ $node["Get User Token"].json.access_token }}

Firebase Pros & Cons

:white_check_mark: Pros:

  • Fast setup (2-3 days for MVP)
  • Excellent GCP integration
  • Automatic scaling
  • Built-in security with GCP KMS
  • Cost-effective at scale

:x: Cons:

  • GCP vendor lock-in
  • Limited customization options
  • Firebase-specific learning curve

Cost estimate: $5-15/month for 100 users, $30-80 for 1K users


Solution 2: Auth0 + Custom Backend

The enterprise-grade solution for complex requirements

Why Auth0?

Auth0 handles the heavy lifting of OAuth flows, user management, and compliance. Perfect for enterprise environments with strict security requirements.

Implementation

1. Auth0 configuration:

// Auth0 Dashboard setup
- Application Type: Single Page Application
- Allowed Callbacks: https://your-backend.com/auth/callback  
- Social Connections: Google, Microsoft, GitHub, Slack
- Scopes: Custom per provider

2. Custom backend (Node.js + Express):

// routes/auth.js
router.get('/connect/:provider', async (req, res) => {
  const { provider } = req.params;
  const { user_id } = req.query;
  
  // Build Auth0 authorization URL
  const authUrl = `https://${process.env.AUTH0_DOMAIN}/authorize?` +
    `response_type=code&` +
    `client_id=${process.env.AUTH0_CLIENT_ID}&` +
    `redirect_uri=${encodeURIComponent(process.env.CALLBACK_URL)}&` +
    `connection=${provider}&` +
    `state=${user_id}`;
    
  res.redirect(authUrl);
});

router.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  const user_id = state;
  
  // Exchange code for tokens via Auth0
  const tokenResponse = await auth.oauth.authorizationCodeGrant({
    code,
    redirect_uri: process.env.CALLBACK_URL
  });
  
  // Get user identity from Auth0 Management API
  const userInfo = await management.getUser({ id: user_id });
  const identity = userInfo.identities.find(id => id.connection === provider);
  
  // Store encrypted credentials in your database
  await prisma.userCredential.upsert({
    where: { userId_provider: { userId: user_id, provider } },
    update: {
      accessToken: await encrypt(identity.access_token),
      refreshToken: await encrypt(identity.refresh_token),
      expiresAt: new Date(Date.now() + (3600 * 1000))
    },
    create: {
      userId: user_id,
      provider,
      accessToken: await encrypt(identity.access_token),
      refreshToken: await encrypt(identity.refresh_token),
      expiresAt: new Date(Date.now() + (3600 * 1000))
    }
  });
  
  res.json({ success: true });
});

3. n8n API endpoint:

// routes/credentials.js
router.get('/credentials/:user_id/:provider', authenticateN8n, async (req, res) => {
  const { user_id, provider } = req.params;
  
  let credential = await prisma.userCredential.findUnique({
    where: { userId_provider: { userId: user_id, provider } }
  });
  
  if (!credential) {
    return res.status(404).json({ 
      error: 'No credentials found',
      oauth_url: `${process.env.BACKEND_URL}/auth/connect/${provider}?user_id=${user_id}`
    });
  }
  
  // Auto-refresh if needed
  if (credential.expiresAt < new Date()) {
    credential = await refreshTokenIfNeeded(credential);
  }
  
  res.json({
    access_token: await decrypt(credential.accessToken),
    expires_at: credential.expiresAt.toISOString()
  });
});

Auth0 Pros & Cons

:white_check_mark: Pros:

  • Enterprise-grade security (SOC2, HIPAA, GDPR)
  • 30+ OAuth providers supported
  • Advanced features (MFA, user management)
  • Professional support and SLAs
  • Comprehensive audit logs

:x: Cons:

  • Higher cost ($50-100+ per month)
  • More complex architecture
  • Requires custom backend development
  • Auth0 dependency

Cost estimate: $70-150/month for 100 users, $300-600 for 1K users


Solution 3: Supabase

The modern, developer-friendly option

Why Supabase?

Supabase combines the simplicity of Firebase with the power of PostgreSQL. It’s open-source, has excellent developer experience, and can be self-hosted.

Implementation

1. Database setup:

-- Supabase SQL Editor
CREATE TABLE user_credentials (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id),
  provider TEXT NOT NULL,
  access_token TEXT NOT NULL,
  refresh_token TEXT,
  expires_at TIMESTAMP WITH TIME ZONE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  UNIQUE(user_id, provider)
);

-- Row Level Security
ALTER TABLE user_credentials ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can only access their own credentials"
ON user_credentials FOR ALL 
TO authenticated
USING (auth.uid() = user_id);

2. OAuth flow (Edge Functions):

// supabase/functions/oauth-callback/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  const { code, state } = new URL(req.url).searchParams
  const [userId, provider] = state.split('_')
  
  // Exchange code for tokens (provider-specific logic)
  const tokens = await exchangeCodeForTokens(code, provider)
  
  // Store in Supabase
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )
  
  const { error } = await supabase
    .from('user_credentials')
    .upsert({
      user_id: userId,
      provider,
      access_token: encrypt(tokens.access_token),
      refresh_token: encrypt(tokens.refresh_token),
      expires_at: new Date(Date.now() + tokens.expires_in * 1000).toISOString()
    })
  
  if (error) throw error
  
  return new Response('OAuth successful!', { status: 200 })
})

3. n8n integration:

// Direct REST API call in n8n HTTP Request node
Method: GET
URL: https://your-project.supabase.co/rest/v1/user_credentials
Headers:
  Authorization: Bearer YOUR_SUPABASE_SERVICE_KEY
  apikey: YOUR_SUPABASE_ANON_KEY
Query Parameters:
  user_id: eq.{{ $parameter.user_id }}
  provider: eq.google
  select: access_token,expires_at

Supabase Pros & Cons

:white_check_mark: Pros:

  • Fastest setup (MVP in 1 day)
  • Excellent developer experience
  • Open source (self-hostable)
  • Very cost-effective
  • Real-time features included

:x: Cons:

  • Newer platform (less mature)
  • Smaller ecosystem
  • Limited enterprise features
  • Manual OAuth provider setup

Cost estimate: $0-25/month for 100 users, $25-100 for 1K users


Choosing the Right Solution

Go with Firebase if:

  • You’re already using Google Cloud Platform
  • You want the fastest path to production with minimal ops
  • Cost optimization is important
  • You need tight GCP service integration

Go with Auth0 if:

  • You have enterprise security/compliance requirements
  • You need advanced user management features
  • Budget allows for premium solutions
  • You want professional support and SLAs

Go with Supabase if:

  • You’re building an MVP or startup project
  • You prefer open-source solutions
  • You want modern developer experience
  • Initial budget is limited

Implementation Tips

1. Start Simple

Begin with one OAuth provider (Google) and basic token storage. Add complexity gradually.

2. Security First

  • Always encrypt tokens at rest
  • Use secure API keys for n8n authentication
  • Implement proper error handling
  • Add audit logging

3. Handle Edge Cases

// In your n8n workflows, always check for missing credentials
if (!$node["Get User Token"].json.access_token) {
  // Handle missing credentials gracefully
  const oauthUrl = $node["Get User Token"].json.oauth_url;
  throw new Error(`Please authorize at: ${oauthUrl}`);
}

4. Monitor and Alert

Set up monitoring for:

  • Token refresh failures
  • API rate limits
  • Authentication errors
  • User connection status

Real-World Usage

Here’s how this looks in a production n8n workflow:

// 1. Get user credentials
HTTP Request: GET /api/credentials/user123/google
Response: { "access_token": "ya29.a0...", "expires_at": "..." }

// 2. Use credentials for Gmail API
HTTP Request: GET https://gmail.googleapis.com/gmail/v1/users/me/messages
Headers: Authorization: Bearer {{ $node["Get Credentials"].json.access_token }}

// 3. Process emails and trigger other workflows
// The rest of your n8n automation logic...

Conclusion

Multi-user OAuth in n8n isn’t impossible—it just requires thinking outside the box. By building a dedicated OAuth service that handles user authentication and token management, you can create scalable, secure automation platforms that serve thousands of users.

The key is choosing the right foundation for your specific needs:

  • Firebase for GCP integration and operational simplicity
  • Auth0 for enterprise requirements and advanced features
  • Supabase for modern development and cost efficiency

Start with an MVP using your chosen solution, validate with real users, then iterate and scale. The n8n community is building amazing things, and proper multi-user OAuth opens up entirely new possibilities for automation platforms.


What multi-user challenges are you facing with n8n? Share your experiences and questions in the comments below!

About the Author: Building automation platforms and SaaS products with n8n. Follow for more technical deep-dives on workflow automation and integration patterns.


Resources & Links

Tags: #n8n #oauth #automation #firebase #auth0 supabase #saas #workflows #integration

1 Like

Solving Multi-User OAuth in n8n: A Complete Guide to Dynamic Credentials

How to build scalable, secure OAuth credential management for multi-tenant n8n workflows


If you’ve ever tried to build a SaaS application or multi-user automation platform with n8n, you’ve probably hit this wall: how do you handle different users’ credentials dynamically?

n8n’s credential system works great for single-user scenarios, but when you need user-specific API access tokens that refresh automatically, things get complex fast. After building several production systems facing this exact challenge, I’m sharing three battle-tested solutions that actually work.

The Multi-User OAuth Problem

Let’s say you’re building a productivity platform where users can automate their Gmail, Google Drive, and Slack. Each user needs to authenticate with their own accounts, but n8n credentials are static and workflow-scoped. You can’t do this:

// ❌ This doesn't work in n8n credentials
Authorization: Bearer {{ $json.user_token }}

The credential fields are resolved before your workflow execution context exists, so dynamic data from previous nodes isn’t available.

What We Need

  1. User-specific OAuth flows - Each user authorizes your app
  2. Secure token storage - Encrypted, isolated per user
  3. Automatic token refresh - No manual intervention required
  4. n8n integration - Seamless credential fetching in workflows
  5. Multi-provider support - Google, Microsoft, GitHub, Slack, etc.

Solution Architecture

All successful implementations follow this pattern:

User Frontend → OAuth Service → Third-party APIs
      ↓              ↓              ↓
   n8n Workflows ← Token API ← Secure Storage

The key insight: external OAuth service + n8n HTTP requests for credential fetching.


Solution 1: Firebase Auth + Firestore

Perfect for teams already using Google Cloud Platform

Why Firebase?

Firebase gives you OAuth infrastructure, secure storage, and cloud functions out-of-the-box. If you’re already on GCP, this is the most cost-effective and operationally simple solution.

Implementation

1. Set up Firestore collections:

// Collection: userCredentials
{
  "user123_google": {
    userId: "user123",
    provider: "google",
    accessToken: "encrypted_token_here",
    refreshToken: "encrypted_refresh_token",
    expiresAt: "2025-05-25T15:30:00Z",
    scopes: ["gmail.readonly", "drive.readonly"]
  }
}

2. Create Cloud Functions for OAuth:

// Cloud Function: initiateOAuth
exports.initiateOAuth = functions.https.onRequest(async (req, res) => {
  const { userId, provider } = req.query;
  
  // Generate OAuth URL with proper scopes
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: ['https://www.googleapis.com/auth/gmail.readonly'],
    state: `${userId}_${provider}`,
    prompt: 'consent'
  });
  
  res.json({ authUrl });
});

// Cloud Function: oauthCallback  
exports.oauthCallback = functions.https.onRequest(async (req, res) => {
  const { code, state } = req.query;
  const [userId, provider] = state.split('_');
  
  // Exchange code for tokens
  const { tokens } = await oauth2Client.getToken(code);
  
  // Encrypt and store in Firestore
  await db.collection('userCredentials').doc(`${userId}_${provider}`).set({
    userId,
    provider, 
    accessToken: await encrypt(tokens.access_token),
    refreshToken: await encrypt(tokens.refresh_token),
    expiresAt: new Date(tokens.expiry_date)
  });
  
  res.send('✅ OAuth connection successful!');
});

3. API endpoint for n8n:

// Cloud Function: getUserCredentials
exports.getUserCredentials = functions.https.onRequest(async (req, res) => {
  const { userId, provider } = req.params;
  
  let credential = await db.collection('userCredentials')
    .doc(`${userId}_${provider}`).get();
  
  if (!credential.exists) {
    return res.status(404).json({ 
      error: 'No credentials found',
      oauthUrl: `https://your-functions-url/initiateOAuth?userId=${userId}&provider=${provider}`
    });
  }
  
  const data = credential.data();
  
  // Refresh token if needed
  if (data.expiresAt.toDate() < new Date()) {
    await refreshToken(userId, provider, data);
    // Fetch updated data
    credential = await credential.ref.get();
  }
  
  const decryptedToken = await decrypt(data.accessToken);
  
  res.json({
    access_token: decryptedToken,
    expires_at: data.expiresAt.toDate().toISOString()
  });
});

4. n8n integration:

// HTTP Request node in n8n
Method: GET
URL: https://your-region-project.cloudfunctions.net/getUserCredentials/{{ $parameter.user_id }}/google
Headers:
  Authorization: Bearer YOUR_API_KEY

// Use the response in subsequent nodes
Authorization: Bearer {{ $node["Get User Token"].json.access_token }}

Firebase Pros & Cons

:white_check_mark: Pros:

  • Fast setup (2-3 days for MVP)
  • Excellent GCP integration
  • Automatic scaling
  • Built-in security with GCP KMS
  • Cost-effective at scale

:cross_mark: Cons:

  • GCP vendor lock-in
  • Limited customization options
  • Firebase-specific learning curve

Cost estimate: $5-15/month for 100 users, $30-80 for 1K users


Solution 2: Auth0 + Custom Backend

The enterprise-grade solution for complex requirements

Why Auth0?

Auth0 handles the heavy lifting of OAuth flows, user management, and compliance. Perfect for enterprise environments with strict security requirements.

Implementation

1. Auth0 configuration:

// Auth0 Dashboard setup
- Application Type: Single Page Application
- Allowed Callbacks: https://your-backend.com/auth/callback  
- Social Connections: Google, Microsoft, GitHub, Slack
- Scopes: Custom per provider

2. Custom backend (Node.js + Express):

// routes/auth.js
router.get('/connect/:provider', async (req, res) => {
  const { provider } = req.params;
  const { user_id } = req.query;
  
  // Build Auth0 authorization URL
  const authUrl = `https://${process.env.AUTH0_DOMAIN}/authorize?` +
    `response_type=code&` +
    `client_id=${process.env.AUTH0_CLIENT_ID}&` +
    `redirect_uri=${encodeURIComponent(process.env.CALLBACK_URL)}&` +
    `connection=${provider}&` +
    `state=${user_id}`;
    
  res.redirect(authUrl);
});

router.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  const user_id = state;
  
  // Exchange code for tokens via Auth0
  const tokenResponse = await auth.oauth.authorizationCodeGrant({
    code,
    redirect_uri: process.env.CALLBACK_URL
  });
  
  // Get user identity from Auth0 Management API
  const userInfo = await management.getUser({ id: user_id });
  const identity = userInfo.identities.find(id => id.connection === provider);
  
  // Store encrypted credentials in your database
  await prisma.userCredential.upsert({
    where: { userId_provider: { userId: user_id, provider } },
    update: {
      accessToken: await encrypt(identity.access_token),
      refreshToken: await encrypt(identity.refresh_token),
      expiresAt: new Date(Date.now() + (3600 * 1000))
    },
    create: {
      userId: user_id,
      provider,
      accessToken: await encrypt(identity.access_token),
      refreshToken: await encrypt(identity.refresh_token),
      expiresAt: new Date(Date.now() + (3600 * 1000))
    }
  });
  
  res.json({ success: true });
});

3. n8n API endpoint:

// routes/credentials.js
router.get('/credentials/:user_id/:provider', authenticateN8n, async (req, res) => {
  const { user_id, provider } = req.params;
  
  let credential = await prisma.userCredential.findUnique({
    where: { userId_provider: { userId: user_id, provider } }
  });
  
  if (!credential) {
    return res.status(404).json({ 
      error: 'No credentials found',
      oauth_url: `${process.env.BACKEND_URL}/auth/connect/${provider}?user_id=${user_id}`
    });
  }
  
  // Auto-refresh if needed
  if (credential.expiresAt < new Date()) {
    credential = await refreshTokenIfNeeded(credential);
  }
  
  res.json({
    access_token: await decrypt(credential.accessToken),
    expires_at: credential.expiresAt.toISOString()
  });
});

Auth0 Pros & Cons

:white_check_mark: Pros:

  • Enterprise-grade security (SOC2, HIPAA, GDPR)
  • 30+ OAuth providers supported
  • Advanced features (MFA, user management)
  • Professional support and SLAs
  • Comprehensive audit logs

:cross_mark: Cons:

  • Higher cost ($50-100+ per month)
  • More complex architecture
  • Requires custom backend development
  • Auth0 dependency

Cost estimate: $70-150/month for 100 users, $300-600 for 1K users


Solution 3: Supabase

The modern, developer-friendly option

Why Supabase?

Supabase combines the simplicity of Firebase with the power of PostgreSQL. It’s open-source, has excellent developer experience, and can be self-hosted.

Implementation

1. Database setup:

-- Supabase SQL Editor
CREATE TABLE user_credentials (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id),
  provider TEXT NOT NULL,
  access_token TEXT NOT NULL,
  refresh_token TEXT,
  expires_at TIMESTAMP WITH TIME ZONE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  UNIQUE(user_id, provider)
);

-- Row Level Security
ALTER TABLE user_credentials ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can only access their own credentials"
ON user_credentials FOR ALL 
TO authenticated
USING (auth.uid() = user_id);

2. OAuth flow (Edge Functions):

// supabase/functions/oauth-callback/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  const { code, state } = new URL(req.url).searchParams
  const [userId, provider] = state.split('_')
  
  // Exchange code for tokens (provider-specific logic)
  const tokens = await exchangeCodeForTokens(code, provider)
  
  // Store in Supabase
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )
  
  const { error } = await supabase
    .from('user_credentials')
    .upsert({
      user_id: userId,
      provider,
      access_token: encrypt(tokens.access_token),
      refresh_token: encrypt(tokens.refresh_token),
      expires_at: new Date(Date.now() + tokens.expires_in * 1000).toISOString()
    })
  
  if (error) throw error
  
  return new Response('OAuth successful!', { status: 200 })
})

3. n8n integration:

// Direct REST API call in n8n HTTP Request node
Method: GET
URL: https://your-project.supabase.co/rest/v1/user_credentials
Headers:
  Authorization: Bearer YOUR_SUPABASE_SERVICE_KEY
  apikey: YOUR_SUPABASE_ANON_KEY
Query Parameters:
  user_id: eq.{{ $parameter.user_id }}
  provider: eq.google
  select: access_token,expires_at

Supabase Pros & Cons

:white_check_mark: Pros:

  • Fastest setup (MVP in 1 day)
  • Excellent developer experience
  • Open source (self-hostable)
  • Very cost-effective
  • Real-time features included

:cross_mark: Cons:

  • Newer platform (less mature)
  • Smaller ecosystem
  • Limited enterprise features
  • Manual OAuth provider setup

Cost estimate: $0-25/month for 100 users, $25-100 for 1K users


Choosing the Right Solution

Go with Firebase if:

  • You’re already using Google Cloud Platform
  • You want the fastest path to production with minimal ops
  • Cost optimization is important
  • You need tight GCP service integration

Go with Auth0 if:

  • You have enterprise security/compliance requirements
  • You need advanced user management features
  • Budget allows for premium solutions
  • You want professional support and SLAs

Go with Supabase if:

  • You’re building an MVP or startup project
  • You prefer open-source solutions
  • You want modern developer experience
  • Initial budget is limited

Implementation Tips

1. Start Simple

Begin with one OAuth provider (Google) and basic token storage. Add complexity gradually.

2. Security First

  • Always encrypt tokens at rest
  • Use secure API keys for n8n authentication
  • Implement proper error handling
  • Add audit logging

3. Handle Edge Cases

// In your n8n workflows, always check for missing credentials
if (!$node["Get User Token"].json.access_token) {
  // Handle missing credentials gracefully
  const oauthUrl = $node["Get User Token"].json.oauth_url;
  throw new Error(`Please authorize at: ${oauthUrl}`);
}

4. Monitor and Alert

Set up monitoring for:

  • Token refresh failures
  • API rate limits
  • Authentication errors
  • User connection status

Real-World Usage

Here’s how this looks in a production n8n workflow:

// 1. Get user credentials
HTTP Request: GET /api/credentials/user123/google
Response: { "access_token": "ya29.a0...", "expires_at": "..." }

// 2. Use credentials for Gmail API
HTTP Request: GET https://gmail.googleapis.com/gmail/v1/users/me/messages
Headers: Authorization: Bearer {{ $node["Get Credentials"].json.access_token }}

// 3. Process emails and trigger other workflows
// The rest of your n8n automation logic...

Conclusion

Multi-user OAuth in n8n isn’t impossible—it just requires thinking outside the box. By building a dedicated OAuth service that handles user authentication and token management, you can create scalable, secure automation platforms that serve thousands of users.

The key is choosing the right foundation for your specific needs:

  • Firebase for GCP integration and operational simplicity
  • Auth0 for enterprise requirements and advanced features
  • Supabase for modern development and cost efficiency

Start with an MVP using your chosen solution, validate with real users, then iterate and scale. The n8n community is building amazing things, and proper multi-user OAuth opens up entirely new possibilities for automation platforms.


What multi-user challenges are you facing with n8n? Share your experiences and questions in the comments below!

About the Author: Building automation platforms and SaaS products with n8n. Follow for more technical deep-dives on workflow automation and integration patterns.


Resources & Links

Tags: #n8n #oauth #automation #firebase #auth0 supabase #saas #workflows #integration

Multitenant Google Calendar Integration with n8n: The Easy Way

Imagine you have an app where each user wants to connect and use their own Google Calendar. You want one smooth experience for connecting and managing calendars. Here’s how to do that—broken down into clear steps.

1. What You Need

  • Frontend app: The website your users log into.
  • n8n instance: For managing automation and connecting to Google.
  • Database: To securely store each user’s Google tokens (their special calendar “keys”).

2. Simple User Workflow

Step A: User Connects Google Calendar (One-Time Only)

  • In your app, users see a “Connect Google Calendar” button.
  • When they click it, your front end sends them to Google’s consent screen asking “Do you want to allow this app to access your calendar?”
  • If they say yes, your app (via n8n) receives a special “access token” and “refresh token.”
  • These tokens are saved securely in your database—linked to this user.

Example:

  • User Sarah logs in for the first time.
  • She clicks “Connect Google Calendar.”
  • She sees Google’s screen, clicks “Allow.”
  • Her Google tokens are stored in your system.
  • She never needs to do this again, unless she revokes access.

Step B: User Uses the App as Normal

  • Sarah now wants to book a meeting or view events.
  • She logs in as usual and uses your app.
  • When she asks for calendar info or booking, your app (or n8n) automatically fetches her saved Google token from the database.
  • n8n uses this token to talk to Google Calendar on her behalf—no extra steps for Sarah!

Example:

  • Sarah clicks “See My Events.”
  • Your frontend calls your n8n workflow, passing her user ID.
  • n8n checks your database for Sarah’s token.
  • n8n sends a request to Google Calendar, using her token.
  • Google replies with Sarah’s events.
  • n8n sends the info back to your frontend, which shows her calendar.

3. Key Technical Steps (Behind the Scenes)

  • Your single frontend checks:
    • “Is there a saved Google token for this user?”
      • No? Prompt them to connect their Google account (Step A).
      • Yes? Proceed with calendar actions (Step B).
  • n8n workflows:
    • One for managing the OAuth connection and saving tokens.
    • One for calendar actions (fetch/create events) using the stored token.
  • Tokens:
    • Access token: Used to access Google Calendar. Lasts about 1 hour.
    • Refresh token: Used by your app to get a new access token (behind the scenes).
    • User only repeats the “Connect” flow if their token is revoked or expires.

4. Simple Visual Explanation

text

User logs in (single frontend)
    |
    |---> If NOT connected: "Connect Google Calendar" (one-time)
        ---> User approves with Google
        ---> Tokens saved in database
        ---> Now ready!
    |
    |---> If connected: User books/reads calendar events
        ---> Frontend passes user ID to n8n
        ---> n8n gets user's token from database
        ---> n8n calls Google Calendar via HTTP node using that token
        ---> Calendar data sent back to frontend (displayed to user)

5. Sample Code (HTTP Request to Google Calendar)

If you’re using n8n’s HTTP node, set headers like this:

  • Authorization: Bearer <access_token>
  • Accept: application/json

Sample n8n HTTP node config:

Name Value
Method GET
URL https://www.googleapis.com/calendar/v3/calendars/primary/events
Headers Authorization: Bearer {{$json[“accessToken”]}}
Accept: application/json

6. What Users Experience

  • The first time: Connect Google Calendar (one click, only once).
  • After that: Use the app, book events, read calendars—every time, the app talks to their calendar automatically.

7. Summary Table

Action User Sees Behind the Scenes
Connect account (once) Connect Google button Receive/save token in DB via n8n auth workflow
Use calendar features Book/view events (normal UI) Fetch token, use HTTP node, send result via n8n
2 Likes

I like this solution. I’m actually looking for a solution like this for a small company where they want to monitor all of the users’ inboxes and send them notifications. I need a single workflow that can work with multiple credentials, and I am considering implementing this.

I have a question: You mentioned that the token lasts for an hour. Does that mean that the user must refresh the token manually every hour or so? Maybe there’s something I’m not understanding. I know that if I set up a production app in the Google Cloud Platform, the authentication should last about a year before it needs to be renewed.

L

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.