mcpField
N
node-add-oauth

node-add-oauth

github.com/n8n-io開発208.9k59 回インストール今日更新
リポジトリ

node-add-oauth について

既存の n8n ノードに標準的な OAuth2 認可コード方式を追加するワークフロー向けスキルです。エンドポイント調査から認証情報、ルーティング、テスト、検証までを案内します。

主な機能

  1. 01既存ノードと認証情報の規約を確認
  2. 02OAuth2 エンドポイントとスコープを調査
  3. 03認証情報の定義と登録
  4. 04ノードと認証ルーティングを更新
  5. 05認証情報とルーティングのテストを追加
  6. 06型チェック、Lint、テストを実行

活用シーン

  1. 01n8n の連携機能に OAuth2 認証を追加
  2. 02認証情報でカスタムスコープをサポート
  3. 03ゲートウェイ経由の OAuth ルーティングを実装
  4. 04テストで OAuth 認証情報の動作を検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/n8n-io/n8n --skill node-add-oauth

インストール後、Claude Code で/node-add-oauthを入力して読み込まれたか確認します。

詳細

概要

既存の n8n ノードに OAuth2(Authorization Code/3LO)のサポートを追加します。標準的な OAuth2 をサポートするあらゆるサードパーティサービスで使用できます。

作業を始める前に、packages/nodes-base/credentials/ にある既存の OAuth2 認証情報ファイルとテストを読み、このコードベースで採用されている規約を確認してください(例:DiscordOAuth2Api.credentials.ts、MicrosoftTeamsOAuth2Api.credentials.ts)。


ステップ 0 — 引数を解析する

以下を抽出します。

  • NODE_NAME:サービス名(例:GitHub、Notion)。引数から推測してください。不明確な場合はユーザーに確認してください。
  • CUSTOM_SCOPES:認証情報がユーザー定義のスコープをサポートする必要があるかどうか。引数から判断できない場合は、作業を進める前にユーザーに確認してください。

    「ユーザーがこの認証情報の OAuth2 スコープをカスタマイズできるようにしますか。それともスコープを固定しますか?」


ステップ 1 — ノードを調査する

以下を読みます(対象サービスに合わせてパスの規約を調整してください)。

  1. ノードディレクトリ:packages/nodes-base/nodes/{NODE_NAME}/
    • *.node.ts(メインノード)と、*Trigger.node.ts があれば確認します。
    • GenericFunctions.ts を探します(別の名前の場合があります)。
    • auth/version サブディレクトリがあるか確認します。
  2. 既存の認証情報:packages/nodes-base/credentials/ — {NODE_NAME}*Api.credentials.ts に該当する既存ファイルを探し、命名規約と、すでに使用されている認証方式を確認します。
  3. packages/nodes-base/package.json にある package.json — このノードの既存の認証情報が登録されている場所を探します(ノード名を grep します)。

ステップ 2 — OAuth2 エンドポイントを調査する

サービスの OAuth2 ドキュメントを確認します。

  • 認可 URL
  • アクセストークン URL
  • 必須の認証クエリパラメーター(例:prompt=consent、access_type=offline)
  • ノードの既存の操作に必要なデフォルトスコープ
  • トークン交換後に API が cloudId/ワークスペース ID の検索を必要とするかどうか(Atlassian 形式のゲートウェイ API では必要ですが、多くのサービスでは不要です)

エンドポイントを確実に特定できない場合は、ユーザーに提示を依頼してください。


ステップ 3 — 認証情報ファイルを作成する

ファイル: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(' ') + '"}}',
		},
	];
}

ルール:

  • authenticate ブロックは不要です。oAuth2Api の仕組みが Bearer トークンの挿入を自動的に処理します。
  • test ブロックは不要です。OAuth のやり取りで認証情報を検証します。
  • モジュールレベルの defaultScopes を唯一の信頼できる情報源とします。これは enabledScopes のデフォルト値と、scope 式のフォールバック値の両方に使われます。変更する場合は、1 か所だけ更新してください。
  • API 呼び出しの構築にドメイン/ワークスペース URL が必要な場合は、非表示フィールドの前に、表示される string フィールドとして追加します。

ステップ 4 — package.json に認証情報を登録する

ファイル:packages/nodes-base/package.json

n8n.credentials 配列を探し、このサービスのほかの認証情報の近くに新しい項目を挿入します(サービス内ではアルファベット順に並べます)。

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

ステップ 5 — GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE を更新する(カスタムスコープのみ)

このステップは CUSTOM_SCOPES = yes の場合にのみ実行してください。

ファイル:packages/cli/src/constants.ts

GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE 配列に '{camelCase}OAuth2Api' を追加します。これを追加しないと、OAuth2 再接続時に n8n がユーザーのカスタムスコープを削除します。

export const GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE = [
	'oAuth2Api',
	'googleOAuth2Api',
	'microsoftOAuth2Api',
	'highLevelOAuth2Api',
	'mcpOAuth2Api',
	'{camelCase}OAuth2Api', // ← add this
];

ステップ 6 — GenericFunctions.ts を更新する

6a — 標準サービス(トークンをインスタンス URL に直接使用できる場合)

既存の else フォールバックの前に else if 分岐を追加します。

} else if ({versionParam} === '{camelCase}OAuth2') {
	domain = (await this.getCredentials('{camelCase}OAuth2Api')).{domainField} as string;
	credentialType = '{camelCase}OAuth2Api';
} else {
6b — ワークスペース/cloud ID の検索が必要なゲートウェイサービス

OAuth トークンのスコープが直接のインスタンス URL ではなくゲートウェイ URL に設定されている場合(代表例は Atlassian の api.atlassian.com)、モジュールレベルのキャッシュと検索ヘルパーを、メインのリクエスト関数の前に追加します。

// 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;
}

次に、メインのリクエスト関数に追加します。

} 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 {

既存の uri: \${domain}/rest${endpoint}`` の構築により、正しいゲートウェイ URL が自動的に生成されます。

まだ追加されていない場合は、n8n-workflow のインポートに NodeOperationError を追加します。

ステップ 7 — ノードファイルを更新する

メインノード(*.node.ts)

Credentials 配列 — 新しい認証情報タイプのエントリーを追加します。

{
	name: '{camelCase}OAuth2Api',
	required: true,
	displayOptions: { show: { {versionParam}: ['{camelCase}OAuth2'] } },
},

バージョン/認証オプション — {versionParam}(または同等のもの)のオプションリストに追加します。

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

default は変更しないでください。既存のワークフローに影響してはなりません。

トリガーノード(*Trigger.node.ts、存在する場合)

同じ 2 つの変更を行います。トリガーノードの credentials 配列にある他の認証情報エントリーで使用されている displayName のラベルパターンを維持してください。


ステップ 8 — 認証情報テストを作成する

ファイル:packages/nodes-base/credentials/test/{NODE_NAME}OAuth2Api.credentials.test.ts

@n8n/client-oauth2 の ClientOAuth2 と、HTTP モック用の nock を使用します。 MicrosoftTeamsOAuth2Api.credentials.test.ts の構成に従ってください。

必須のテストケース:

  1. メタデータ — name、extends 配列、enabledScopes のデフォルト値、認証 URL、トークン URL、authQueryParameters のデフォルト値(該当する場合)。
  2. 認証 URI のデフォルトスコープ — oauthClient.code.getUri() を呼び出し、各デフォルトスコープが含まれていることをアサートします。
  3. デフォルトスコープでのトークン取得 — nock でトークンエンドポイントをモックし、oauthClient.code.getToken(...) を呼び出して、token.data.scope に各スコープが含まれていることをアサートします。
  4. 認証 URI のカスタムスコープ(CUSTOM_SCOPES = no の場合はスキップ)。
  5. カスタムスコープでのトークン取得(CUSTOM_SCOPES = no の場合はスキップ)。
  6. 最小/異なるスコープセット(CUSTOM_SCOPES = no の場合はスキップ) — セットに含まれないスコープが URI とトークンレスポンスの両方に存在しないことをアサートします。

必須のライフサイクルフック:

beforeAll(() => { nock.disableNetConnect(); });
afterAll(() => { nock.restore(); });
afterEach(() => { nock.cleanAll(); });

ステップ 9 — GenericFunctions.test.ts を更新する

認証情報のルーティングに関する describe ブロックで、次の変更を行います。

  1. サイト ID キャッシュ(_cloudIdCache)を追加した場合は、それをインポートし、afterEach で _cloudIdCache.clear()(または同等の処理)を呼び出します。
  2. OAuth2 のルーティングテストケースを追加/更新します。
    • シンプルなルーティング:getCredentials が正しい認証情報名で呼び出され、requestWithAuthentication が正しい名前と URI で呼び出されたことをアサートします。
    • ゲートウェイの検索:最初の呼び出しではアクセス可能なリソースのペイロードを、2 回目の呼び出しでは {} を返すように requestWithAuthentication をモックします。最初の呼び出しがリソースエンドポイントを対象とし、2 回目の呼び出しがサイト ID を含むゲートウェイのベース URL を使用することをアサートします。

ステップ 10 — 検証する

# 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

完了する前に、型エラーを修正してください。pnpm typecheck は決して省略しないでください。

コメント