AutomationFlowsWeb Scraping › Automatic Google Cloud Run Auth with Jwt Token Management (sub-workflow)

Automatic Google Cloud Run Auth with Jwt Token Management (sub-workflow)

ByMarco Cassar @marcocassar on n8n.io

Anyone calling a Google Cloud Run service from n8n who wants a small, reusable auth layer instead of wiring tokens in every workflow.

Event trigger★★★★☆ complexity16 nodesJwtExecute Workflow TriggerHTTP Request
Web Scraping Trigger: Event Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #7569 — we link there as the canonical source.

This workflow follows the Execute Workflow Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "4cb478c7-57e8-4028-a39c-0a47cd3edb0a",
      "name": "If Token",
      "type": "n8n-nodes-base.if",
      "position": [
        224,
        544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "37fd6852-3507-40ed-bf5e-d55d22d7fd7c",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id_token }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "30a6ed20-9f0a-481f-825a-47affdc14a90",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        608,
        416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "88d8edde-ab2c-45b3-9abb-1d70d518656a",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ ($json.payload.exp * 1000) - 300000}}",
              "rightValue": "={{ Date.now() }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "27cebdb1-e3c5-445c-99fe-421b43a8535b",
      "name": "Return Values",
      "type": "n8n-nodes-base.set",
      "position": [
        1232,
        560
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8607e929-d7b7-4ed5-86ca-234dab37a4e0",
              "name": "id_token",
              "type": "string",
              "value": "={{ $json.id_token }}"
            },
            {
              "id": "04db663c-0a3e-4254-be2f-8086ea4f43e7",
              "name": "service_url",
              "type": "string",
              "value": "={{ $('Combine Context').item.json.service_url }}"
            },
            {
              "id": "8cfaf785-9736-4828-9203-20bdb23f6eb8",
              "name": "service_path",
              "type": "string",
              "value": "={{ $('Combine Context').item.json.service_path }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1ff1b7b4-69d1-45de-bdc3-425781ac4fda",
      "name": "Combine Context",
      "type": "n8n-nodes-base.merge",
      "position": [
        0,
        544
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "dd9fbe7d-c064-40be-9afe-bd1c65844c4c",
      "name": "Raise Id",
      "type": "n8n-nodes-base.set",
      "position": [
        880,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "294b0c8a-81d9-47d1-a938-11cd6585bf1c",
              "name": "id_token",
              "type": "string",
              "value": "={{ $('Combine Context').item.json.id_token }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e15593e0-3831-4642-83a4-f1262a781c43",
      "name": "Decode JWT",
      "type": "n8n-nodes-base.jwt",
      "position": [
        416,
        416
      ],
      "parameters": {
        "token": "={{ $json.id_token }}",
        "options": {},
        "operation": "decode"
      },
      "credentials": {
        "jwtAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cc318a16-9e8e-4b8c-a9dc-518b4119e783",
      "name": "Sign New JWT",
      "type": "n8n-nodes-base.jwt",
      "position": [
        784,
        560
      ],
      "parameters": {
        "options": {},
        "useJson": true,
        "claimsJson": "={\n  \"iss\": \"{{ $json.client_email }}\",\n  \"sub\": \"{{ $json.client_email }}\",\n  \"aud\": \"{{ $json.token_uri }}\",\n  \"iat\": {{ Math.floor(Date.now() / 1000) }},\n  \"exp\": {{ Math.floor(Date.now() / 1000) + 3600 }},\n  \"target_audience\": \"{{ $json.service_url }}\"\n}"
      },
      "credentials": {
        "jwtAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9dfe02d4-ca20-449f-98af-98233233f54f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -736,
        192
      ],
      "parameters": {
        "width": 400,
        "height": 496,
        "content": "## Google Service Auth Workflow\n\n## **Purpose**\nHandles Google Cloud authentication with automatic token refresh. Checks if existing token is valid before requesting new one.\n\n## **How It Works**\n1. Receives optional `id_token`, `service_url`, and `service_path` input\n2. Checks token validity (5-min buffer)\n3. Reuses valid token OR generates new\n4. Returns Bearer YOUR_TOKEN_HERE for Cloud Run Service calls\n\n## **Output**\n* `id_token`: Valid Bearer YOUR_TOKEN_HERE\n* `service_url`: Your API endpoint\n* `service_path`: API path"
      },
      "typeVersion": 1
    },
    {
      "id": "ff9df5f7-eac1-449e-b772-e5e8ec029623",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -16
      ],
      "parameters": {
        "color": 4,
        "width": 304,
        "height": 512,
        "content": "## Using This Workflow\n\n### **As Sub-Workflow**\nCall from main workflow using Execute Workflow node:\n* Optional Input: `id_token` (for reuse)\n* Returns: Fresh or validated token\n\n### **Token Logic**\n* **Has Valid Token**: Returns existing\n* **Token Expiring Soon**: Gets new token\n* **No Token**: Generates fresh token\n\n#### **5-Minute Buffer**\nToken refreshes if expires within 5 minutes to prevent mid-execution expiration\n\n### **Test Standalone**\n1. Required: Pin `service_url` value."
      },
      "typeVersion": 1
    },
    {
      "id": "f99ed6c6-01a2-480e-bc89-508c098b951a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        0
      ],
      "parameters": {
        "color": 5,
        "width": 256,
        "height": 384,
        "content": "## Key Features\n### **Automatic Validation**\n* Decodes JWT to check expiration\n* No unnecessary API calls\n* Reduces quota usage\n### **Error Prevention**\n* 5-minute expiration buffer\n* Prevents mid-workflow failures\n* Ensures token validity\n### **Efficient Reuse**\n* Tokens valid for 60 minutes\n* Reuses existing valid tokens\n* Only refreshes when needed"
      },
      "typeVersion": 1
    },
    {
      "id": "2e0584c6-8628-49db-b2fa-6ae7488cff41",
      "name": "Start",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -448,
        560
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "id_token"
            },
            {
              "name": "service_url"
            },
            {
              "name": "service_path"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "1995c7ce-b6ca-4c99-b683-73c2fa7581f8",
      "name": "Bearer YOUR_TOKEN_HERE Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1008,
        560
      ],
      "parameters": {
        "url": "={{ $('Vars').item.json.token_uri }}",
        "body": "=grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={{ $json.token }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "specifyBody": "string"
      },
      "typeVersion": 4.2
    },
    {
      "id": "d98fd0cd-cd67-4290-9aa1-d0cbf8d98bb9",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        352
      ],
      "parameters": {
        "color": 2,
        "width": 166,
        "height": 240,
        "content": "#### Var Config\n- **client_email** - from `.json key`"
      },
      "typeVersion": 1
    },
    {
      "id": "2c38cc10-1c06-45fc-8283-3a4f3afede0e",
      "name": "Vars",
      "type": "n8n-nodes-base.set",
      "position": [
        -224,
        448
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n  \"service_url\": \"{{ $json.service_url }}\",\n  \"client_email\": \"_____INSERT_____\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\"\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "ff91e405-5cdc-4857-bc83-166a18342c64",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        544
      ],
      "parameters": {
        "color": 2,
        "width": 230,
        "height": 432,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n### Create a JWT Credential\n\nUse the **private_key** from your generated `.json key` file.\n\n- **Key Type:** PEM Key  \n- **Private Key:** `private_key` (full block, including `-----BEGIN PRIVATE KEY-----` \u2026 `END PRIVATE KEY-----`)  \n- **Algorithm:** RS256"
      },
      "typeVersion": 1
    },
    {
      "id": "2f44ed65-9dbe-4713-9153-3c9e49893d19",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -32
      ],
      "parameters": {
        "color": 2,
        "width": 672,
        "height": 208,
        "content": "## Required Setup \u2014 Google Cloud Run & Service Account\n### You\u2019ll need to:\n- **Configure a Google Cloud Run service** (set **Require authentication**).\n- **Create a Google Service Account** and grant it **Cloud Run Invoker** permission on that service.\n\nFor a detailed, step-by-step breakdown, see my Medium article:\n\n**[Build a Secure Google Cloud Run API, Then Call It from n8n (Free Tier)](https://medium.com/@marcocodes/build-a-secure-google-cloud-run-api-then-call-it-from-n8n-88c03291a95f)**"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Raise Id",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sign New JWT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Vars": {
      "main": [
        [
          {
            "node": "Combine Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start": {
      "main": [
        [
          {
            "node": "Combine Context",
            "type": "main",
            "index": 1
          },
          {
            "node": "Vars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Token": {
      "main": [
        [
          {
            "node": "Decode JWT",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sign New JWT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Raise Id": {
      "main": [
        [
          {
            "node": "Return Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decode JWT": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sign New JWT": {
      "main": [
        [
          {
            "node": "Bearer YOUR_TOKEN_HERE Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Context": {
      "main": [
        [
          {
            "node": "If Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bearer Token Request": {
      "main": [
        [
          {
            "node": "Return Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Anyone calling a Google Cloud Run service from n8n who wants a small, reusable auth layer instead of wiring tokens in every workflow.

Source: https://n8n.io/workflows/7569/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This template is a powerful, reusable utility for managing stateful, long-running processes. It allows a main workflow to be paused indefinitely at "checkpoints" and then be resumed by external, async

HTTP Request, Execute Workflow Trigger
Web Scraping

Upload files from any source to your account Kommo or AmoCRM with a simple and reusable workflow. It can split a large file into small ones and upload chunks. Works for Kommo and amoCRM There are 3 re

HTTP Request, Execute Workflow Trigger, Stop And Error
Web Scraping

Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.

HTTP Request, GitHub, Execute Workflow Trigger +1
Web Scraping

Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.

Execute Workflow Trigger, HTTP Request, GitHub
Web Scraping

This workflow audits your SharePoint Online environment for external sharing risks by identifying files and folders that are shared with anonymous links or external/guest users. It is designed to trav

HTTP Request, Execute Workflow Trigger