OAuth2 for Beginners: Connecting APIs Without the Pain
What You’ll Need
- n8n Cloud or self-hosted n8n instance
- Hetzner VPS or Contabo VPS for self-hosting
- A code editor (VS Code recommended)
- OAuth2-enabled service account (Google, GitHub, or Spotify)
- Basic understanding of HTTP requests and JSON
Table of Contents
- Understanding OAuth2 Flow
- Setting Up Your First OAuth2 Application
- Implementing OAuth2 in n8n
- Common OAuth2 Patterns and Troubleshooting
- Getting Started
Understanding OAuth2 Flow
I remember the first time I tried to integrate with an API that required OAuth2. I expected a simple username and password, and instead got hit with authorization codes, token endpoints, and refresh tokens. It felt like overkill until I realized: OAuth2 isn’t designed for your app to hoard user credentials—it’s designed so you never touch them.
Here’s what actually happens:
- User clicks “Connect” → They’re redirected to the OAuth provider (Google, GitHub, etc.)
- User logs in and grants permission → The provider knows they approved your app
- Provider sends back a code → Your app trades this code for an access token
- Access token lets you act on their behalf → But only for what they permitted
The genius part? Your app never sees their password. They revoke access whenever they want. And providers can monitor what your app does.
OAuth2 has four main flows, but we’re focusing on the Authorization Code Flow—the one you’ll use 90% of the time for web apps and automation platforms.
Setting Up Your First OAuth2 Application
Let’s walk through registering an OAuth2 app with Google—the most common starting point.
Step 1: Create a Google Cloud Project
Head to Google Cloud Console . Click the project dropdown at the top and select “New Project.”
Name it something memorable:
Project Name: My Automation Bot
Organization: (optional)
Hit Create. Google takes 30 seconds to spin it up.
Step 2: Enable the Google Drive API
Once in your project, go to APIs & Services → Library. Search for “Google Drive API” and click Enable.
Do the same for Google Sheets API and Gmail API. We’ll use these later.
Step 3: Create OAuth2 Credentials
Navigate to APIs & Services → Credentials. Click + Create Credentials → OAuth Client ID.
Google will ask you to configure a consent screen first. Click Configure Consent Screen:
User Type: External
App name: My Automation Bot
User support email: your-email@gmail.com
Developer contact: your-email@gmail.com
Add scopes for the APIs you enabled. For Gmail, add:
https://www.googleapis.com/auth/gmail.readonlyhttps://www.googleapis.com/auth/gmail.send
Save and continue.
Now go back to Credentials → + Create Credentials → OAuth Client ID → Web application.
Add Authorized Redirect URIs:
http://localhost:3000/callback
https://yourdomain.com/callback
https://n8n.yourdomain.com/callback
Google generates a Client ID and Client Secret. Save these immediately—you won’t see the secret again.
💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.
Implementing OAuth2 in n8n
Now let’s wire this into n8n . I’ll show you both the visual workflow builder and the raw configuration.
Method 1: Using n8n’s Built-In OAuth2 Credentials
The easiest path: n8n has pre-configured OAuth2 for major services. Here’s how:
- Create a new workflow in n8n Cloud or your self-hosted instance
- Add a node → Search for “Gmail”
- Click Authenticate when prompted
- Authorize the pop-up that opens
- Done. n8n handles the token exchange automatically.
Under the hood, n8n stores your refresh token encrypted and rotates access tokens before they expire.
Method 2: Custom OAuth2 with HTTP Requests
Sometimes you need to wire up a custom API. Here’s the complete flow using n8n’s HTTP Request node.
Create a workflow with these nodes in sequence:
Node 1: Webhook (Trigger)
This receives the authorization code from the OAuth provider:
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [100, 200],
"webhookId": "your-webhook-id",
"method": "GET",
"path": "oauth-callback"
}
Node 2: Set Variables
Extract the authorization code from the query parameter:
{
"name": "Set Authorization Code",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [400, 200],
"assignments": [
{
"name": "authCode",
"value": "={{ $query.code }}"
},
{
"name": "state",
"value": "={{ $query.state }}"
}
]
}
Node 3: Exchange Code for Token
This is the critical step. We POST to the OAuth provider’s token endpoint:
{
"name": "Get Access Token",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [700, 200],
"method": "POST",
"url": "https://oauth.example.com/token",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "grant_type",
"value": "authorization_code"
},
{
"name": "code",
"value": "={{ $node[\"Set Authorization Code\"].json.authCode }}"
},
{
"name": "client_id",
"value": "YOUR_CLIENT_ID"
},
{
"name": "client_secret",
"value": "YOUR_CLIENT_SECRET"
},
{
"name": "redirect_uri",
"value": "https://n8n.yourdomain.com/webhook/oauth-callback"
}
]
}
}
Node 4: Store Token Securely
Use n8n’s Credential system instead of plaintext storage. Create a new Credential:
Settings → Credentials → + Create:
{
"credentialType": "oAuth2Api",
"name": "Custom OAuth2",
"data": {
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"accessToken": "={{ $node[\"Get Access Token\"].json.access_token }}",
"refreshToken": "={{ $node[\"Get Access Token\"].json.refresh_token }}",
"expiresIn": "={{ $node[\"Get Access Token\"].json.expires_in }}"
}
}
Node 5: Use the Access Token
Now you can make authenticated requests:
{
"name": "Get User Profile",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1000, 200],
"method": "GET",
"url": "https://api.example.com/user/profile",
"headers": {
"Authorization": "Bearer {{ $credentials.oAuth2Api.accessToken }}"
}
}
Handling Token Refresh
Access tokens expire (usually after 1 hour). n8n automatically refreshes them, but here’s what happens behind the scenes:
{
"name": "Refresh Token If Needed",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1300, 200],
"conditions": {
"nodeVersion": 2,
"conditions": [
{
"id": "condition_0",
"leftValue": "={{ Date.now() }}",
"rightValue": "={{ $credentials.oAuth2Api.expiresAt }}",
"operator": {
"type": "boolean",
"properties": {
"operation": "greaterThan"
}
}
}
]
}
}
If expired, call the refresh endpoint:
{
"name": "Refresh Access Token",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1300, 400],
"method": "POST",
"url": "https://oauth.example.com/token",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "grant_type",
"value": "refresh_token"
},
{
"name": "refresh_token",
"value": "={{ $credentials.oAuth2Api.refreshToken }}"
},
{
"name": "client_id",
"value": "YOUR_CLIENT_ID"
},
{
"name": "client_secret",
"value": "YOUR_CLIENT_SECRET"
}
]
}
}
Common OAuth2 Patterns and Troubleshooting
Pattern 1: Multi-Tenant OAuth (SaaS)
If you’re building a platform where each user connects their own service:
- Store tokens per user in your database with encryption
- Add a user ID to the state parameter to match tokens on callback
- Use Namecheap to register a domain, then set up a reverse proxy with SSL to handle redirect URIs securely
Here’s the state parameter pattern:
{
"name": "Generate State",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"assignments": [
{
"name": "state",
"value": "={{ Buffer.from(JSON.stringify({ userId: 123, nonce: Math.random() })).toString('base64') }}"
}
]
}
Pattern 2: Background Token Refresh
For long-running workflows, refresh tokens proactively every 50 minutes:
{
"name": "Schedule Token Refresh",
"type": "n8n-nodes-base.cron",
"typeVersion": 1,
"triggerTimes": {
"mode": "everyX",
"value": 50,
"unit": "minutes"
}
}
Common Errors and Fixes
“Invalid redirect URI”
- Ensure the redirect URI in Google Console exactly matches what n8n sends
- Include protocol (http/https) and port if applicable
- No trailing slashes
“Invalid client secret”
- You copied it wrong. Go back to Google Console and regenerate.
- Check for hidden whitespace.
“Token expired” errors usually mean the refresh token itself has expired (typically after 6 months of inactivity) or the user revoked access. Handle this by catching the error and redirecting back to the authorization step:
{
"name": "Catch Token Error",
"type": "n8n-nodes-base.catchError",
"typeVersion": 1,
"position": [1600, 200]
}
Add error handling to your HTTP request nodes:
{
"name": "Get User Profile with Error Handler",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1000, 200],
"method": "GET",
"url": "https://api.example.com/user/profile",
"headers": {
"Authorization": "Bearer {{ $credentials.oAuth2Api.accessToken }}"
},
"errorHandler": {
"type": "manual",
"errorResponses": [
{
"status": "401",
"action": "redirectToAuth"
}
]
}
}
If a 401 comes back, trigger re-authentication. Store the refresh token with a timestamp and check it before making requests.
Getting Started
You’re ready to move from theory to production. Here’s your toolkit:
- n8n Cloud – Start here if you want zero infrastructure overhead. Built-in OAuth2 handling, hosted webhooks, and you pay per execution.
- Self-hosted alternative – Use Hetzner VPS or DigitalOcean if you need full control and want to keep data on your own servers.
- Domain and DNS – Grab a domain from Namecheap to set up professional redirect URIs instead of localhost.
Start with a simple integration—maybe Google Drive or Gmail. Get the token exchange working, then layer in refresh logic and error handling. Once that’s solid, you’ll recognize the same patterns everywhere.
The complexity isn’t in OAuth2 itself; it’s in the details. But now you’ve got the complete flow wired up.
Outsource Your Automation
Don’t have time? I build production n8n workflows, WhatsApp bots, and fully automated YouTube Shorts pipelines. Hire me on Fiverr — mention SYS3-HUGO for priority. Or DM at chasebot.online .
Want to automate this yourself?
Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.
Want this engine running on your own VPS?
This blog publishes itself — daily, unattended, on free API tiers. The full engine, Hugo theme, and setup guide are available as System 3.
Get System 3