
n8n OAuth2 Node Add
FreeEasily integrate OAuth2 support into n8n nodes.
Free · Opens the source repo
What n8n OAuth2 Node Add does
The n8n OAuth2 Node Add skill enables developers to seamlessly add OAuth2 credential support to existing n8n nodes. This skill is particularly useful for integrating third-party services that utilize the OAuth2 standard for authentication. By automating the creation of credential files, updating node configurations, and managing associated tests, this skill streamlines the process of enhancing n8n workflows with secure authentication methods.
To utilize the skill, users will need to provide the service name and indicate whether custom scopes should be supported. The skill intelligently parses these inputs and guides users through the necessary steps, including exploring existing node structures and researching the relevant OAuth2 endpoints for the service in question. This ensures that the integration adheres to best practices within the n8n ecosystem.
Once the required information is gathered, the skill generates a credential file that includes all necessary properties for OAuth2 authentication, such as authorization and access token URLs. It also manages the registration of the new credential in the package.json file, ensuring that the integration is recognized by n8n. For services that allow custom scopes, the skill includes additional configuration to maintain user-defined settings during OAuth2 reconnections.
This skill is ideal for developers working with n8n who need to enhance their automation workflows by integrating services that require OAuth2 authentication. It reduces the manual effort involved in setting up these integrations and ensures compliance with n8n's coding standards.
When to use it
Use this skill when you need to integrate a third-party service that requires OAuth2 authentication into your n8n workflows.
When not to use it
This skill is not suitable for services that do not support OAuth2 or for users who prefer to handle OAuth2 integration manually.
What you can build with it
Integrating GitHub with n8n
Use this skill to add OAuth2 support for GitHub, allowing automated workflows to authenticate and interact with GitHub APIs.
Connecting Notion to n8n
Easily integrate Notion's API with OAuth2 authentication to automate data management tasks in your n8n workflows.
Enhancing Atlassian API Access
Add OAuth2 support to your n8n nodes for Atlassian services, ensuring secure access to project management tools.
How to install n8n OAuth2 Node Add
View source1. Install with the skills CLI
npx skills add n8n-io/n8n/node-add-oauth --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by n8n-ioOverview
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):
- 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/versionsubdirectory exists
- Find
- Existing credentials:
packages/nodes-base/credentials/— look for existing{NODE_NAME}*Api.credentials.tsfiles to understand the naming convention and any auth method already in use. package.jsonatpackages/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
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
const defaultScopes = [/* minimum scopes for existing node operations */];
export class {NODE_NAME}OAuth2Api implements ICredentialType {
name = '{camelCase}OAuth2Api';
extends = ['oAuth2Api'];
displayName = '{Display Name} OAuth2 API';
documentationUrl = '{doc-slug}'; // matches docs.n8n.io/integrations/...
properties: INodeProperties[] = [
// Include service-specific fields the node needs to construct API calls
// (e.g. domain, workspace URL) — add BEFORE the hidden fields below.
{ displayName: 'Grant Type', name: 'grantType', type: 'hidden', default: 'authorizationCode' },
{ displayName: 'Authorization URL', name: 'authUrl', type: 'hidden', default: '{AUTH_URL}', required: true },
{ displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden', default: '{TOKEN_URL}', required: true },
// Only include authQueryParameters if the service requires extra query params:
{ displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden', default: '{QUERY_PARAMS}' },
{ displayName: 'Authentication', name: 'authentication', type: 'hidden', default: 'header' },
// ── Custom scopes block (ONLY when CUSTOM_SCOPES = yes) ──────────────
{
displayName: 'Custom Scopes',
name: 'customScopes',
type: 'boolean',
default: false,
description: 'Define custom scopes',
},
{
displayName:
'The default scopes needed for the node to work are already set. If you change these the node may not function correctly.',
name: 'customScopesNotice',
type: 'notice',
default: '',
displayOptions: { show: { customScopes: [true] } },
},
{
displayName: 'Enabled Scopes',
name: 'enabledScopes',
type: 'string',
displayOptions: { show: { customScopes: [true] } },
default: defaultScopes.join(' '),
description: 'Scopes that should be enabled',
},
// ── End custom scopes block ───────────────────────────────────────────
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
// Custom scopes: expression toggles between user value and defaults.
// Fixed scopes: use the literal defaultScopes string instead.
default:
'={{$self["customScopes"] ? $self["enabledScopes"] : "' + defaultScopes.join(' ') + '"}}',
},
];
}
Rules:
- No
authenticateblock —oAuth2Apimachinery handles Bearer token injection automatically. - No
testblock — the OAuth dance validates the credential. defaultScopesat module level is the single source of truth: it populates both theenabledScopesdefault and thescopeexpression fallback. Update it in one place.- If the service needs a domain / workspace URL for API call construction, add it as a
visible
stringfield 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):
"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.
export const GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE = [
'oAuth2Api',
'googleOAuth2Api',
'microsoftOAuth2Api',
'highLevelOAuth2Api',
'mcpOAuth2Api',
'{camelCase}OAuth2Api', // ← add this
];
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:
} else if ({versionParam} === '{camelCase}OAuth2') {
domain = (await this.getCredentials('{camelCase}OAuth2Api')).{domainField} as string;
credentialType = '{camelCase}OAuth2Api';
} 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:
// Module-level cache: normalised domain → site/cloud ID
export const _cloudIdCache = new Map<string, string>();
async function getSiteId(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
credentialType: string,
domain: string,
): Promise<string> {
const normalizedDomain = domain.replace(/\/$/, '');
if (_cloudIdCache.has(normalizedDomain)) return _cloudIdCache.get(normalizedDomain)!;
const resources = (await this.helpers.requestWithAuthentication.call(this, credentialType, {
uri: '{ACCESSIBLE_RESOURCES_ENDPOINT}',
json: true,
})) as Array<{ id: string; url: string }>;
const site = resources.find((r) => r.url === normalizedDomain);
if (!site) {
throw new NodeOperationError(
this.getNode(),
`No accessible site found for domain: ${domain}. Make sure the domain matches your site URL exactly.`,
);
}
_cloudIdCache.set(normalizedDomain, site.id);
return site.id;
}
Then in the main request function:
} else if ({versionParam} === '{camelCase}OAuth2') {
const rawDomain = (await this.getCredentials('{camelCase}OAuth2Api')).domain as string;
credentialType = '{camelCase}OAuth2Api';
const siteId = await getSiteId.call(this, credentialType, rawDomain);
domain = `{GATEWAY_BASE_URL}/${siteId}`;
} 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:
{
name: '{camelCase}OAuth2Api',
required: true,
displayOptions: { show: { {versionParam}: ['{camelCase}OAuth2'] } },
},
Version/auth options — add to the {versionParam} (or equivalent) options list:
{ 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:
- Metadata — name, extends array,
enabledScopesdefault, auth URL, token URL,authQueryParametersdefault (if applicable). - Default scopes in authorization URI — call
oauthClient.code.getUri(), assert each default scope is present. - Token retrieval with default scopes — mock the token endpoint with
nock, calloauthClient.code.getToken(...), asserttoken.data.scopecontains each scope. - Custom scopes in authorization URI (skip when CUSTOM_SCOPES = no).
- Token retrieval with custom scopes (skip when CUSTOM_SCOPES = no).
- 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:
beforeAll(() => { nock.disableNetConnect(); });
afterAll(() => { nock.restore(); });
afterEach(() => { nock.cleanAll(); });
Step 9 — Update GenericFunctions.test.ts
In the credential-routing describe block:
- If a site-ID cache (
_cloudIdCache) was added, import it and call_cloudIdCache.clear()(or equivalent) inafterEach. - Add/update the OAuth2 routing test case:
- Simple routing: assert
getCredentialswas called with the correct credential name andrequestWithAuthenticationwas called with the correct name and URI. - Gateway lookup: mock
requestWithAuthenticationto 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.
- Simple routing: assert
Step 10 — Verify
# From packages/nodes-base/
pnpm test credentials/test/{NODE_NAME}OAuth2Api.credentials.test.ts
pnpm test nodes/{NODE_NAME}/__test__/GenericFunctions.test.ts
pnpm typecheck
pnpm lint
# Only when constants.ts was changed:
pushd ../cli && pnpm typecheck && popd
Fix any type errors before finishing. Never skip pnpm typecheck.
Frequently asked questions about n8n OAuth2 Node Add
Similar skills
WinMD API Search
Easily find and explore Windows desktop APIs.
WebMCPify
Transform any web app into an agent-ready platform.
Phoenix Tracing
Instrument LLM applications with OpenInference tracing.
Foundry Hosted Agent CopilotKit
Guidance for developing agentic web apps on Azure.
Power Automate Foundation
Connect AI agents to Power Automate seamlessly.
Power Automate Flow Builder
Efficiently build and deploy Power Automate flows programmatically.
