OAuth2 for Beginners: Connecting APIs Without the Pain

APIs #oauth2#api#authentication#webdev#tutorial
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

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:

  1. User clicks “Connect” → They’re redirected to the OAuth provider (Google, GitHub, etc.)
  2. User logs in and grants permission → The provider knows they approved your app
  3. Provider sends back a code → Your app trades this code for an access token
  4. 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 & ServicesLibrary. 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 & ServicesCredentials. Click + Create CredentialsOAuth 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.readonly
  • https://www.googleapis.com/auth/gmail.send

Save and continue.

Now go back to Credentials+ Create CredentialsOAuth Client IDWeb 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:

  1. Create a new workflow in n8n Cloud or your self-hosted instance
  2. Add a node → Search for “Gmail”
  3. Click Authenticate when prompted
  4. Authorize the pop-up that opens
  5. 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:

SettingsCredentials+ 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:

  1. Store tokens per user in your database with encryption
  2. Add a user ID to the state parameter to match tokens on callback
  3. 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”

Want to automate this yourself?

Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.

📬 Get Weekly Automation Tips

One email per week with tutorials, tools, and workflows. No spam, unsubscribe anytime.

Subscribe Free →