node-add-oauth — for Claude Code node-add-oauth, official, n8n workflow automation, n8n skill, for Claude Code, ide skills, n8n node automation, OAuth2 integration, API connection management, credential file creation, Claude Code

v1.0.0
GitHub

About this Skill

node-add-oauth is a skill that automates OAuth2 credential setup for n8n nodes, enabling seamless integration with third-party services.

Related Paths

Features

Create credential files using OAuth2 standards
Update n8n nodes with OAuth2 credential support
Add tests for OAuth2 credential functionality
Keep CLI constants in sync with OAuth2 configurations
Research OAuth2 endpoints for service integration
Parse arguments for node and credential setup

# Core Topics

n8n-io n8n-io
[181.8k]
[56328]
Updated: 3/31/2026
Labs Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox

node-add-oauth

Streamline OAuth2 credential management for n8n nodes with this AI agent skill, designed for developers to automate workflow integrations and API connections.

SKILL.md
Readonly

Overview

Add OAuth2 (Authorization Code / 3LO) support to an existing n8n node. Works for any third-party service that supports standard OAuth2.

Before starting, read comparable existing OAuth2 credential files and tests under packages/nodes-base/credentials/ to understand the conventions used in this codebase (e.g. DiscordOAuth2Api.credentials.ts, MicrosoftTeamsOAuth2Api.credentials.ts).


Step 0 — Parse arguments

Extract:

  • NODE_NAME: the service name (e.g. GitHub, Notion). Try to infer from the argument; if ambiguous, ask the user.
  • CUSTOM_SCOPES: whether the credential should support user-defined scopes. If the argument does not make this clear, ask the user before proceeding:

    "Should users be able to customise the OAuth2 scopes for this credential, or should scopes be fixed?"


Step 1 — Explore the node

Read the following (adjust path conventions for the specific service):

  1. Node directory: packages/nodes-base/nodes/{NODE_NAME}/
    • Find *.node.ts (main node) and any *Trigger.node.ts
    • Find GenericFunctions.ts (may be named differently)
    • Check if an auth / version subdirectory exists
  2. Existing credentials: packages/nodes-base/credentials/ — look for existing {NODE_NAME}*Api.credentials.ts files to understand the naming convention and any auth method already in use.
  3. package.json at packages/nodes-base/package.json — find where existing credentials for this node are registered (grep for the node name).

Step 2 — Research OAuth2 endpoints

Look up the service's OAuth2 documentation:

  • Authorization URL
  • Access Token URL
  • Required auth query parameters (e.g. prompt=consent, access_type=offline)
  • Default scopes needed for the node's existing operations
  • Whether the API requires a cloudId / workspace ID lookup after the token exchange (Atlassian-style gateway APIs do; most services don't)

If you can't determine the endpoints confidently, ask the user to provide them.


Step 3 — Create the credential file

File: packages/nodes-base/credentials/{NODE_NAME}OAuth2Api.credentials.ts

typescript
1import type { ICredentialType, INodeProperties } from 'n8n-workflow'; 2 3const defaultScopes = [/* minimum scopes for existing node operations */]; 4 5export class {NODE_NAME}OAuth2Api implements ICredentialType { 6 name = '{camelCase}OAuth2Api'; 7 extends = ['oAuth2Api']; 8 displayName = '{Display Name} OAuth2 API'; 9 documentationUrl = '{doc-slug}'; // matches docs.n8n.io/integrations/... 10 11 properties: INodeProperties[] = [ 12 // Include service-specific fields the node needs to construct API calls 13 // (e.g. domain, workspace URL) — add BEFORE the hidden fields below. 14 15 { displayName: 'Grant Type', name: 'grantType', type: 'hidden', default: 'authorizationCode' }, 16 { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden', default: '{AUTH_URL}', required: true }, 17 { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden', default: '{TOKEN_URL}', required: true }, 18 // Only include authQueryParameters if the service requires extra query params: 19 { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden', default: '{QUERY_PARAMS}' }, 20 { displayName: 'Authentication', name: 'authentication', type: 'hidden', default: 'header' }, 21 22 // ── Custom scopes block (ONLY when CUSTOM_SCOPES = yes) ────────────── 23 { 24 displayName: 'Custom Scopes', 25 name: 'customScopes', 26 type: 'boolean', 27 default: false, 28 description: 'Define custom scopes', 29 }, 30 { 31 displayName: 32 'The default scopes needed for the node to work are already set. If you change these the node may not function correctly.', 33 name: 'customScopesNotice', 34 type: 'notice', 35 default: '', 36 displayOptions: { show: { customScopes: [true] } }, 37 }, 38 { 39 displayName: 'Enabled Scopes', 40 name: 'enabledScopes', 41 type: 'string', 42 displayOptions: { show: { customScopes: [true] } }, 43 default: defaultScopes.join(' '), 44 description: 'Scopes that should be enabled', 45 }, 46 // ── End custom scopes block ─────────────────────────────────────────── 47 48 { 49 displayName: 'Scope', 50 name: 'scope', 51 type: 'hidden', 52 // Custom scopes: expression toggles between user value and defaults. 53 // Fixed scopes: use the literal defaultScopes string instead. 54 default: 55 '={{$self["customScopes"] ? $self["enabledScopes"] : "' + defaultScopes.join(' ') + '"}}', 56 }, 57 ]; 58}

Rules:

  • No authenticate block — oAuth2Api machinery handles Bearer token injection automatically.
  • No test block — the OAuth dance validates the credential.
  • defaultScopes at module level is the single source of truth: it populates both the enabledScopes default and the scope expression fallback. Update it in one place.
  • If the service needs a domain / workspace URL for API call construction, add it as a visible string field before the hidden fields.

Step 4 — Register the credential in package.json

File: packages/nodes-base/package.json

Find the n8n.credentials array and insert the new entry near other credentials for this service (alphabetical ordering within the service's block):

json
1"dist/credentials/{NODE_NAME}OAuth2Api.credentials.js",

Step 5 — Update GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE (custom scopes only)

Only do this step when CUSTOM_SCOPES = yes.

File: packages/cli/src/constants.ts

Add '{camelCase}OAuth2Api' to the GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE array. Without this, n8n deletes the user's custom scope on OAuth2 reconnect.

typescript
1export const GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE = [ 2 'oAuth2Api', 3 'googleOAuth2Api', 4 'microsoftOAuth2Api', 5 'highLevelOAuth2Api', 6 'mcpOAuth2Api', 7 '{camelCase}OAuth2Api', // ← add this 8];

Step 6 — Update GenericFunctions.ts

6a — Standard services (token works directly against the instance URL)

Add an else if branch before the existing else fallback:

typescript
1} else if ({versionParam} === '{camelCase}OAuth2') { 2 domain = (await this.getCredentials('{camelCase}OAuth2Api')).{domainField} as string; 3 credentialType = '{camelCase}OAuth2Api'; 4} else {

6b — Gateway services requiring a workspace/cloud ID lookup

When the OAuth token is scoped for a gateway URL rather than the direct instance URL (Atlassian's api.atlassian.com is the canonical example), add a module-level cache and lookup helper before the main request function:

typescript
1// Module-level cache: normalised domain → site/cloud ID 2export const _cloudIdCache = new Map<string, string>(); 3 4async function getSiteId( 5 this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, 6 credentialType: string, 7 domain: string, 8): Promise<string> { 9 const normalizedDomain = domain.replace(/\/$/, ''); 10 if (_cloudIdCache.has(normalizedDomain)) return _cloudIdCache.get(normalizedDomain)!; 11 12 const resources = (await this.helpers.requestWithAuthentication.call(this, credentialType, { 13 uri: '{ACCESSIBLE_RESOURCES_ENDPOINT}', 14 json: true, 15 })) as Array<{ id: string; url: string }>; 16 17 const site = resources.find((r) => r.url === normalizedDomain); 18 if (!site) { 19 throw new NodeOperationError( 20 this.getNode(), 21 `No accessible site found for domain: ${domain}. Make sure the domain matches your site URL exactly.`, 22 ); 23 } 24 25 _cloudIdCache.set(normalizedDomain, site.id); 26 return site.id; 27}

Then in the main request function:

typescript
1} else if ({versionParam} === '{camelCase}OAuth2') { 2 const rawDomain = (await this.getCredentials('{camelCase}OAuth2Api')).domain as string; 3 credentialType = '{camelCase}OAuth2Api'; 4 const siteId = await getSiteId.call(this, credentialType, rawDomain); 5 domain = `{GATEWAY_BASE_URL}/${siteId}`; 6} else {

The existing uri: \${domain}/rest${endpoint}`` construction then produces the correct gateway URL automatically.

Add NodeOperationError to the n8n-workflow import if not already present.


Step 7 — Update the node file(s)

Main node (*.node.ts)

Credentials array — add an entry for the new credential type:

typescript
1{ 2 name: '{camelCase}OAuth2Api', 3 required: true, 4 displayOptions: { show: { {versionParam}: ['{camelCase}OAuth2'] } }, 5},

Version/auth options — add to the {versionParam} (or equivalent) options list:

typescript
1{ name: '{Display Name} (OAuth2)', value: '{camelCase}OAuth2' },

Keep default unchanged — existing workflows must not be affected.

Trigger node (*Trigger.node.ts, if present)

Same two changes. Preserve any displayName label pattern already used by other credential entries in that trigger node's credentials array.


Step 8 — Write credential tests

File: packages/nodes-base/credentials/test/{NODE_NAME}OAuth2Api.credentials.test.ts

Use ClientOAuth2 from @n8n/client-oauth2 and nock for HTTP mocking. Follow the structure in MicrosoftTeamsOAuth2Api.credentials.test.ts.

Required test cases:

  1. Metadata — name, extends array, enabledScopes default, auth URL, token URL, authQueryParameters default (if applicable).
  2. Default scopes in authorization URI — call oauthClient.code.getUri(), assert each default scope is present.
  3. Token retrieval with default scopes — mock the token endpoint with nock, call oauthClient.code.getToken(...), assert token.data.scope contains each scope.
  4. Custom scopes in authorization URI (skip when CUSTOM_SCOPES = no).
  5. Token retrieval with custom scopes (skip when CUSTOM_SCOPES = no).
  6. Minimal / different scope set (skip when CUSTOM_SCOPES = no) — assert scopes not in the set are absent from both the URI and token response.

Lifecycle hooks required:

typescript
1beforeAll(() => { nock.disableNetConnect(); }); 2afterAll(() => { nock.restore(); }); 3afterEach(() => { nock.cleanAll(); });

Step 9 — Update GenericFunctions.test.ts

In the credential-routing describe block:

  1. If a site-ID cache (_cloudIdCache) was added, import it and call _cloudIdCache.clear() (or equivalent) in afterEach.
  2. Add/update the OAuth2 routing test case:
    • Simple routing: assert getCredentials was called with the correct credential name and requestWithAuthentication was called with the correct name and URI.
    • Gateway lookup: mock requestWithAuthentication to return the accessible-resources payload on the first call and {} on the second. Assert the first call targets the resources endpoint and the second call uses the gateway base URL with the site ID.

Step 10 — Verify

bash
1# From packages/nodes-base/ 2pnpm test credentials/test/{NODE_NAME}OAuth2Api.credentials.test.ts 3pnpm test nodes/{NODE_NAME}/__test__/GenericFunctions.test.ts 4pnpm typecheck 5pnpm lint 6 7# Only when constants.ts was changed: 8pushd ../cli && pnpm typecheck && popd

Fix any type errors before finishing. Never skip pnpm typecheck.

FAQ & Installation Steps

These questions and steps mirror the structured data on this page for better search understanding.

? Frequently Asked Questions

What is node-add-oauth?

node-add-oauth is a skill that automates OAuth2 credential setup for n8n nodes, enabling seamless integration with third-party services.

How do I install node-add-oauth?

Run the command: npx killer-skills add n8n-io/n8n/node-add-oauth. It works with Cursor, Windsurf, VS Code, Claude Code, and 19+ other IDEs.

Which IDEs are compatible with node-add-oauth?

This skill is compatible with Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer. Use the Killer-Skills CLI for universal one-command installation.

How To Install

  1. 1. Open your terminal

    Open the terminal or command line in your project directory.

  2. 2. Run the install command

    Run: npx killer-skills add n8n-io/n8n/node-add-oauth. The CLI will automatically detect your IDE or AI agent and configure the skill.

  3. 3. Start using the skill

    The skill is now active. Your AI agent can use node-add-oauth immediately in the current project.

Related Skills

Looking for an alternative to node-add-oauth or another official skill for your workflow? Explore these related open-source skills.

View All

flags

Logo of facebook
facebook

Use when you need to check feature flag states, compare channels, or debug why a feature behaves differently across release channels.

243.6k
0
Developer

extract-errors

Logo of facebook
facebook

Use when adding new error messages to React, or seeing unknown error code warnings.

243.6k
0
Developer

fix

Logo of facebook
facebook

Use when you have lint errors, formatting issues, or before committing code to ensure it passes CI.

243.6k
0
Developer

flow

Logo of facebook
facebook

Use when you need to run Flow type checking, or when seeing Flow type errors in React code.

243.6k
0
Developer