n8noauthhttp-requesttutorialtechnical

How to Inject OAuth Tokens Dynamically in n8n Using the HTTP Request Node

Step-by-step technical guide to fetching OAuth tokens at runtime in n8n via HTTP Request node - with JSON config examples for Google, Microsoft and Slack.

CredBridge Team
·March 4, 2025·10 min read

How to Inject OAuth Tokens Dynamically in n8n Using the HTTP Request Node


The most powerful pattern for building scalable, multi-client n8n workflows is also one of the least documented: bypassing n8n's native credential system and injecting OAuth tokens dynamically at runtime via HTTP Request nodes.

This approach breaks the fundamental limitation that forces you to duplicate workflows per client. Once you understand how to implement it, you can build one workflow that authenticates as any of your clients dynamically - no duplicates, no hardcoded credentials.

This guide covers the complete technical implementation: what OAuth tokens are, how they expire, how to fetch them dynamically in n8n, and how to use CredBridge to manage the token store without building one yourself.


OAuth 101: What Tokens Are and Why They Expire

Before diving into the n8n implementation, let's be clear on what we're actually managing.

Access Tokens vs. Refresh Tokens

When a user connects their Google account (or Microsoft, Slack, etc.) via OAuth, you receive two tokens:

Access Token: The short-lived credential that authorizes API requests. Think of it as a temporary keycard. It's what you put in the Authorization: Bearer header when calling APIs.

Refresh Token: The long-lived credential that lets you get new access tokens without asking the user to re-authenticate. Think of it as the master key that generates temporary keycards. Refresh tokens are stored securely and never sent with API requests.

Token Expiry Timelines

Provider Access Token Lifetime Refresh Token Lifetime
Google 1 hour Valid indefinitely (unless revoked)
Microsoft 1 hour (configurable) 90 days (sliding window)
Slack Bot tokens don't expire Workspace tokens: until revoked

The critical insight: Google access tokens expire every 60 minutes. If your workflow runs fine at 9 AM, it may fail at 10 AM with a 401 Unauthorized error - not because anything broke, but because the token aged out.

This is why raw token management is complex: you can't just store an access token once and call it done. You need refresh logic.

What Happens in n8n When a Token Expires

Without token management, here's the failure sequence:

  1. Client's Google OAuth2 credential was set up 2 hours ago
  2. Workflow triggers
  3. Google Calendar node tries to create an event
  4. Google API returns 401: Token has been expired or revoked
  5. Workflow execution fails
  6. Client gets no calendar event
  7. You get no notification (unless you've built error handling)

This is the silent failure mode that frustrates agency clients. And because n8n's native credentials refresh tokens behind the scenes, many builders don't even realize this is happening - until it stops working.


The Dynamic Token Injection Pattern

Here's the core pattern. Instead of using n8n's credential system, you:

  1. Store all OAuth tokens in an external secure store
  2. At workflow execution time, fetch the right token for the current tenant via an HTTP Request node
  3. Use that token via expressions in all downstream nodes

Why This Works

n8n's HTTP Request node supports custom headers via expressions. This means you can do:

Header: Authorization
Value: Bearer {{ $node["Get Token"].json.access_token }}

That {{ $node["Get Token"].json.access_token }} is a standard n8n expression. It references the output of a previous node named "Get Token" and extracts the access_token field. This is fully dynamic - the value changes with every workflow execution based on which tenant triggered it.


Step-by-Step Implementation in n8n

Workflow Structure

[Webhook Trigger] → [Get Token] → [Use Token in API Calls] → [Return Response]

Step 1: Configure the Webhook Trigger

Your webhook payload needs to include a tenantId (or clientId, or whatever identifier you use for your clients):

{
  "tenantId": "client_alice",
  "action": "create_event",
  "title": "Sales Call",
  "startTime": "2026-03-01T10:00:00Z"
}

This comes from your frontend, your CRM, or whatever system triggers the automation.

Step 2: The "Get Token" HTTP Request Node

Add an HTTP Request node immediately after your trigger. Configure it as follows:

Method: GET URL: https://credbridge.app/api/token Authentication: Header Auth Header Name: X-API-Key Header Value: your CredBridge API key Query Parameters:

  • tenantId: {{ $json.tenantId }}

The response will look like:

{
  "access_token": "ya29.A0ARrdaM8k...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "scope": "https://www.googleapis.com/auth/calendar"
}

Name this node exactly "Get Token" (the name matters - you'll reference it in downstream expressions).

Step 3: Use the Token in Downstream Nodes

For any node that needs to make authenticated API calls, use the HTTP Request node (not the native Google Calendar node) and inject the token:

Example: Create a Google Calendar Event

Method: POST
URL: https://www.googleapis.com/calendar/v3/calendars/primary/events
Headers:
  Authorization: Bearer {{ $node["Get Token"].json.access_token }}
  Content-Type: application/json
Body:
{
  "summary": "{{ $json.title }}",
  "start": {
    "dateTime": "{{ $json.startTime }}",
    "timeZone": "UTC"
  },
  "end": {
    "dateTime": "{{ $json.endTime }}",
    "timeZone": "UTC"
  }
}

Example: Send a Slack message to a specific workspace

Method: POST
URL: https://slack.com/api/chat.postMessage
Headers:
  Authorization: Bearer {{ $node["Get Token"].json.slack_token }}
  Content-Type: application/json
Body:
{
  "channel": "#general",
  "text": "New event created: {{ $json.title }}"
}

Example: Send via Microsoft Graph (Outlook)

Method: POST
URL: https://graph.microsoft.com/v1.0/me/sendMail
Headers:
  Authorization: Bearer {{ $node["Get Token"].json.ms_access_token }}
  Content-Type: application/json

The pattern is identical across providers. Only the URL and token field name change.

Step 4: Handle Multiple Providers in One Call

If your workflow needs Google AND Slack AND Microsoft for the same client, you can either:

Option A: Make three separate "Get Token" calls, one per provider, and reference each one by node name.

Option B (CredBridge): Return all tokens for a tenant in a single call:

{
  "google_access_token": "ya29...",
  "slack_token": "xoxb-...",
  "ms_access_token": "eyJ0..."
}

Then reference each with $node["Get Token"].json.google_access_token, etc.


The Token Refresh Challenge

Here's where DIY implementations fall apart. Let's say you build your own token store (a Supabase table, for example). You store refresh tokens per tenant. Your workflow:

  1. Looks up the tenant's stored access token
  2. Checks if it's expired (compares expires_at timestamp to current time)
  3. If expired, calls the Google token refresh endpoint with the refresh token
  4. Stores the new access token + new expiry time
  5. Returns the fresh access token

This works. But consider what happens at scale:

Race conditions: If two executions for the same tenant trigger simultaneously (milliseconds apart), both might see an expired token, both might try to refresh, and you end up with two refresh token exchanges - one of which invalidates the other (Google rotates refresh tokens on use).

Permanent refresh token expiry: Google refresh tokens can expire if unused for 6 months, or if the user revokes access, or if your app's OAuth consent screen changes. You need monitoring for this.

Microsoft's sliding window: Microsoft refresh tokens expire after 90 days of inactivity (they reset each time you use them, but if a client goes quiet for 3 months and then triggers a workflow, their token is gone).

Slack revocation: Slack bot tokens don't expire, but users can revoke them from their workspace settings. You need to detect 401s and notify someone.

This is a meaningful amount of infrastructure to get right. CredBridge handles all of it: concurrent refresh coordination, per-provider refresh rules, expiry detection, and email alerts when a token can't be refreshed.


Using CredBridge as Your Token Store

The fastest path to production is using CredBridge rather than building your own token store. Here's how it integrates:

Setup (One-Time, ~10 Minutes)

  1. Create a CredBridge account
  2. Add your OAuth app credentials (paste client_id + client_secret for Google, Microsoft, Slack)
  3. Copy your CredBridge API key

Client Onboarding

For each client/tenant:

  1. Create a tenant in CredBridge dashboard
  2. Copy the connect link
  3. Send to client: "Click this link, connect your Google account in 30 seconds"
  4. Client authenticates; their tokens are stored encrypted

In Your n8n Workflow

One HTTP Request node. Every token fresh, every time.

GET https://credbridge.app/api/token?tenantId={{ $json.tenantId }}
X-API-Key: your-api-key

CredBridge automatically:

  • Checks if the access token is still valid
  • Refreshes it if it's about to expire (before you hit the 1-hour wall)
  • Returns a fresh, guaranteed-valid token
  • Sends you an email alert if the refresh token is permanently invalid

Error Handling for Token Failures

Even with CredBridge, you should handle cases where a token isn't available (client hasn't connected yet, or has revoked access):

Add an IF node after your "Get Token" call:

Condition: {{ $node["Get Token"].json.access_token }} is not empty
→ TRUE: proceed with workflow
→ FALSE: send error notification / return error response

You can also check the HTTP status code:

  • 200: Token retrieved successfully
  • 404: Tenant not found (client hasn't connected yet)
  • 401: CredBridge API key invalid
  • 503: Token refresh failed (client revoked access)

For the 503 case, trigger a notification workflow that emails the client asking them to reconnect.


Self-Hosted n8n Considerations

This pattern works identically whether you're on n8n.cloud, self-hosted (Docker/VPS), or any other setup. The HTTP Request node is a standard n8n node available everywhere.

One consideration for self-hosted: if your n8n instance is on a private network, make sure outbound requests to credbridge.app are allowed. Firewall rules sometimes block external API calls by default.

For air-gapped environments, CredBridge also supports on-premise deployment (Enterprise plan) - though for most agency setups, the hosted version is simpler and more reliable.


Full Workflow Example

Here's a complete workflow structure for a calendar booking automation serving multiple clients:

1. Webhook Trigger
   → Receives: { tenantId, clientName, meetingTitle, startTime, endTime, guestEmail }

2. Get Token (HTTP Request → CredBridge)
   → Receives: { google_access_token, slack_token }

3. Create Calendar Event (HTTP Request → Google Calendar API)
   → Uses: google_access_token
   → Creates event, gets back eventId

4. Send Invite Email (HTTP Request → Google Gmail API)
   → Uses: google_access_token
   → Sends confirmation to guestEmail

5. Notify Team (HTTP Request → Slack API)
   → Uses: slack_token
   → Posts to client's #sales channel

6. Return Success (Respond to Webhook)
   → Returns: { success: true, eventId }

This one workflow handles 50 clients. Change any step - it applies to all of them immediately.


Summary

The dynamic token injection pattern is the right architectural choice for any n8n workflow that needs to handle multiple clients with separate OAuth accounts. It's not a workaround - it's the correct approach for multi-tenant automation systems.

The main investment is in the token management layer. CredBridge handles that layer completely, so you can focus on building the actual automation logic.

Ready to implement?

Start with CredBridge for free → - first month on us, setup in 10 minutes.


Sources consulted:

Stop duplicating n8n workflows

One workflow for all clients. OAuth tokens managed automatically. From $19/mo.

Start free - first month on us →