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 →
{
"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.
jwtAuth
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
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
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
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.