Git测试
- 作者仓库星标 190,957
- 作者更新于 实时读取
- 作者仓库 n8n
- 领域
- 工程开发
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @n8n-io · 未声明 license
- Token 消耗评级
- 低消耗
- 接入复杂程度
- 需手动接入
- 是否需要外部 API Key
- 需要 · Vendor-specific
- 兼容的系统
- macOS · Linux · Windows
- 底层运行要求
- Node.js
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- Shell 执行
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: n8n:node-add-oauth
description: Add OAuth2 credential support to an existing n8n node — creates the credential file, updates the…
category: 工程开发
runtime: Node.js
---
# n8n:node-add-oauth 输出预览
## PART A: 任务判断
- 适用问题:代码实现、重构、调试或代码审查。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Overview / Step 0 — Parse arguments / Step 1 — Explore the node”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于代码实现、重构、调试或代码审查,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Overview / Step 0 — Parse arguments / Step 1 — Explore the node”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、执行终端命令、会按任务需要访问外部网络、需要准备 Vendor-specific API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、执行终端命令。
先用一个小任务确认它会围绕“Overview / Step 0 — Parse arguments / Step 1 — Explore the node”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: n8n:node-add-oauth
description: Add OAuth2 credential support to an existing n8n node — creates the credential file, updates the…
category: 工程开发
source: n8n-io/n8n
---
# n8n:node-add-oauth
## 什么时候使用
- 用于组织测试、定位失败并形成修复闭环 适合处理工程开发场景下的代码实现、调试、重构、测试或代码审查,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的步骤;使用前要…
- 面向代码实现、重构、调试或代码审查,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Overview / Step 0 — Parse arguments / Step 1 — Explore the node」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、执行终端命令;会按任务需要访问外部网络;需要准备 Vendor-specific API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "n8n:node-add-oauth" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Overview / Step 0 — Parse arguments / Step 1 — Explore the node
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Node.js | 读取文件、写入/修改文件、执行终端命令 | 会按任务需要访问外部网络
安全层 -> 需要准备 Vendor-specific API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} 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):
- 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.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核