oauth-automation
SKILL.md
OAuth Token Automation
When to use: Workflows using OAuth (YouTube, Google, Facebook, etc.) that need automatic token refresh without manual intervention.
Problem Statement
OAuth tokens expire (typically 1 hour), breaking workflows that run on schedules. Manual token refresh via n8n UI is not sustainable for production workflows.
Solutions Overview
| Solution | Reliability | Performance | Complexity | Use Case |
|---|---|---|---|---|
| Real-time Refresh | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | Best for critical workflows |
| Scheduled Update | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | Good for daily workflows |
| Hybrid Mode | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | Recommended balance |
| Hardcoded | ⭐ | ⭐⭐⭐ | ⭐ | ❌ Not recommended |
Solution 1: Real-Time Refresh (Recommended)
Pattern: Workflow refreshes token at start of every execution.
Workflow Structure
[Trigger]
↓
[Get Refresh Token from n8n DB]
Read credential: id=123
↓
[Refresh OAuth Token]
POST to token endpoint
↓
[Store Access Token for Use]
Pass to downstream nodes
↓
[YouTube/Google API Call]
Use fresh access_token
Implementation
// Code Node: Refresh OAuth Token
var https = require('https');
var clientId = process.env.GOOGLE_CLIENT_ID;
var clientSecret = process.env.GOOGLE_CLIENT_SECRET;
var refreshToken = process.env.GOOGLE_REFRESH_TOKEN;
var postData = JSON.stringify({
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken,
grant_type: 'refresh_token'
});
var options = {
hostname: 'oauth2.googleapis.com',
path: '/token',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
var tokenData = await new Promise(function(resolve, reject) {
var req = https.request(options, function(res) {
var data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
if (res.statusCode === 200) {
resolve(JSON.parse(data));
} else {
reject(new Error('Token refresh failed: ' + data));
}
});
});
req.on('error', reject);
req.write(postData);
req.end();
});
return {
json: {
access_token: tokenData.access_token,
expires_in: tokenData.expires_in,
token_type: tokenData.token_type
}
};
// Code Node: Use Token in API Call
var accessToken = $input.first().json.access_token;
var videoId = 'abc123';
var options = {
hostname: 'www.googleapis.com',
path: '/youtube/v3/videos?part=snippet&id=' + videoId,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + accessToken
}
};
var result = await makeRequest(options);
return {json: result};
Pros:
- ✅ Always uses fresh tokens
- ✅ No risk of expiration mid-workflow
- ✅ Works for any schedule
Cons:
- ⚠️ Extra API call per execution (~500ms)
- ⚠️ Refresh token must be secure
Solution 2: Scheduled Update via n8n API
Pattern: Separate workflow updates main workflow code every 30 minutes.
Architecture
[Schedule Trigger: Every 30min]
↓
[Refresh OAuth Token]
↓
[Update Workflow Code via n8n API]
PATCH /workflows/{id}
Replace token in Code node
↓
[Main Workflow Uses Updated Token]
Implementation
// Scheduled Workflow: Update Token in Main Workflow
var https = require('https');
// Step 1: Refresh token (same as Solution 1)
var tokenData = await refreshOAuthToken();
// Step 2: Get current workflow
var workflowId = 'main-workflow-id';
var n8nApiKey = process.env.N8N_API_KEY;
var getOptions = {
hostname: 'localhost',
port: 5678,
path: '/api/v1/workflows/' + workflowId,
method: 'GET',
headers: {
'X-N8N-API-KEY': n8nApiKey
}
};
var workflow = await makeRequest(getOptions);
// Step 3: Update token in Code node
var nodes = workflow.nodes;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].name === 'YouTube API Call') {
var code = nodes[i].parameters.jsCode;
// Replace token in code
var newCode = code.replace(
/const ACCESS_TOKEN = '[^']+'/,
"const ACCESS_TOKEN = '" + tokenData.access_token + "'"
);
nodes[i].parameters.jsCode = newCode;
}
}
// Step 4: Update workflow
var updateOptions = {
hostname: 'localhost',
port: 5678,
path: '/api/v1/workflows/' + workflowId,
method: 'PATCH',
headers: {
'X-N8N-API-KEY': n8nApiKey,
'Content-Type': 'application/json'
}
};
var updateBody = JSON.stringify({nodes: nodes});
await makeRequest(updateOptions, updateBody);
return {json: {success: true, token_expires: tokenData.expires_in}};
Pros:
- ✅ No performance overhead in main workflow
- ✅ Works even if workflow doesn't run for days
Cons:
- ⚠️ Tokens may still expire if schedule misses
- ⚠️ Requires n8n API access
Solution 3: Hybrid Mode (Recommended)
Combines both approaches:
- Scheduled update keeps token fresh in code
- Real-time refresh as fallback on API errors
// Try using token from code
var accessToken = 'token-from-scheduled-update';
try {
var result = await callAPI(accessToken);
return {json: result};
} catch (error) {
if (error.statusCode === 401) {
// Token expired, refresh now
var newToken = await refreshOAuthToken();
var result = await callAPI(newToken.access_token);
return {json: result};
}
throw error;
}
Configuration
Environment Variables
# .env or startup script
export GOOGLE_CLIENT_ID=your_client_id
export GOOGLE_CLIENT_SECRET=your_client_secret
export GOOGLE_REFRESH_TOKEN=1//0xxx... # Long-lived refresh token
export N8N_API_KEY=n8n_api_xxx # For Solution 2
Get Refresh Token
# 1. Get authorization code (browser)
https://accounts.google.com/o/oauth2/v2/auth?
client_id=YOUR_CLIENT_ID&
redirect_uri=http://localhost&
response_type=code&
scope=https://www.googleapis.com/auth/youtube.readonly&
access_type=offline&
prompt=consent
# 2. Exchange code for tokens
curl -X POST https://oauth2.googleapis.com/token \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=http://localhost" \
-d "grant_type=authorization_code"
# Response includes refresh_token (save this!)
Best Practices
- Store Refresh Token Securely: Environment variable, not in code
- Handle Errors: Retry on network failures, alert on auth failures
- Log Token Refresh: Track when tokens are refreshed for debugging
- Use HTTPS: Always use secure connections for token requests
- Rotate Tokens: Periodically regenerate refresh tokens for security
Troubleshooting
Invalid refresh token
Error: invalid_grant
Solution: Re-authorize and get new refresh_token
Token refresh rate limit
Error: rate_limit_exceeded
Solution: Cache access_token, only refresh when needed (check expires_in)
n8n API authentication failed
Error: 401 Unauthorized
Solution: Check N8N_API_KEY is correct and has workflow:write permission
Integration with Other Skills
- video-processing: Use fresh token for YouTube API
- error-handling: Retry on token refresh failures
- notion-operations: Log token refresh events
Full Code and Documentation
Complete implementations:
/mnt/d/work/n8n_agent/n8n-skills/n8n-oauth-automation/
Files:
oauth-token-refresh.js- Token refresh implementationn8n-api-workflow-update.js- Scheduled update patterncode-node-https-example.js- HTTP request templateREADME.md- Complete guide with all solutions
Weekly Installs
1
Repository
aixier/n8n-auto…tion-hubFirst Seen
9 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1