Kintoneメールプラグイン OAuth 2.0移行の技術ガイド — Node.js実装例
Kintoneからメール送信を行うプラグインで、SMTP基本認証をOAuth 2.0に切り替える手順を、コードと設定の具体例とともに解説します。Microsoft 365の基本認証が2026年4月に完全停止する背景を受けて、多くの企業が移行に着手しています。
この記事の要点
OAuth 2.0への移行は、認証方式の変更に加えて、トークン管理・リフレッシュ・スコープ設計・エラーハンドリングの刷新が必要です。新規実装で約3~5日、既存実装の改修で5~10日が目安。この記事では、Node.js + Kintone環境での実装例と、運用時の注意点を具体的に整理します。
移行前の現状把握
まず、現行のプラグインがどの認証方式を使っているか確認します。SMTP基本認証の典型的なコード:
// SMTP基本認証の例(Node.js + nodemailer)
const transporter = nodemailer.createTransport({
host: 'smtp.office365.com',
port: 587,
secure: false,
auth: {
user: 'sender@your-company.com',
pass: 'password_here' // 基本認証 — 2026年4月以降使用不可
}
});
このパターンは、Microsoft 365・Gmail共通で、2024年~2026年の期間に順次使えなくなります。
OAuth 2.0実装の全体像
OAuth 2.0では、以下の4ステップでメール送信が実現されます:
- 認可コード取得:ユーザーをGoogle/Microsoftの認証画面にリダイレクト、認可コードを取得
- トークン交換:認可コードをaccess tokenとrefresh tokenに交換
- トークン保存:暗号化してKintoneアプリまたは自社DBに保存
- API呼び出し:access tokenを使ってGmail API / Microsoft Graph API経由で送信。期限切れ時はrefresh tokenで自動更新
ステップ1:OAuth アプリ登録
Google Cloud側の設定
- Google Cloud Console(console.cloud.google.com)にアクセス
- プロジェクトを作成(例:kinplug-mail-production)
- 「APIとサービス」→「認証情報」で、OAuth 2.0 クライアントIDを作成
- リダイレクトURIに、プラグインのコールバックURL(例:
https://api.your-plugin.com/oauth/google/callback)を登録 - スコープを「
https://www.googleapis.com/auth/gmail.send」に限定(最小権限の原則) - Gmail APIを有効化
Microsoft Entra ID側の設定
- Azure Portal(portal.azure.com)にアクセス
- Entra ID → アプリ登録 → 新規登録
- リダイレクトURIに、プラグインのコールバックURLを登録
- 「APIのアクセス許可」で Microsoft Graph の
Mail.Send、User.Read、offline_accessを追加 - 委任されたアクセス許可を選択(管理者同意が必要な場合あり)
- クライアントシークレットを発行(24ヶ月の有効期限推奨)
ステップ2:認可コードフロー実装
// Node.js + Express例(Microsoft Graph向け)
app.get('/oauth/microsoft/start', (req, res) => {
const params = new URLSearchParams({
client_id: MS_CLIENT_ID,
response_type: 'code',
redirect_uri: 'https://api.your-plugin.com/oauth/microsoft/callback',
scope: 'offline_access Mail.Send User.Read',
state: generateSecureState(req), // CSRF対策
prompt: 'select_account'
});
res.redirect(`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${params}`);
});
app.get('/oauth/microsoft/callback', async (req, res) => {
const { code, state } = req.query;
validateState(state, req); // CSRF検証
const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: MS_CLIENT_ID,
client_secret: MS_CLIENT_SECRET,
code,
redirect_uri: 'https://api.your-plugin.com/oauth/microsoft/callback',
grant_type: 'authorization_code'
})
});
const tokens = await tokenResponse.json();
// tokens.access_token, tokens.refresh_token, tokens.expires_in を保存
await saveTokens(userId, tokens);
res.redirect('/success');
});
ステップ3:トークン保存の設計
トークンは必ず暗号化して保存します。暗号化キーは、プラグインのコードに埋め込まず、環境変数またはKMS経由で取得します。
// AES-256-GCM での暗号化例
const crypto = require('crypto');
function encryptToken(token, key) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key, 'hex'), iv);
const encrypted = Buffer.concat([cipher.update(token, 'utf8'), cipher.final()]);
const authTag = cipher.getAuthTag();
return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted.toString('hex');
}
保存先の選択肢:
- Kintone専用アプリ(推奨):OAuth接続情報管理アプリを1つ作成し、暗号化トークンを保存。アプリへのアクセス権限を厳格に絞る。プラグインからのアクセスもAPIキー経由で制限。
- サーバー側DB:PostgreSQL等の自社DBに保存。ただし、ユーザー情報を自社で保持する法的責任が発生するため、プライバシーポリシーで明示が必要。
- Secrets Manager(AWS等):クラウドベンダーのシークレット管理サービス。コスト効率が低下する可能性あり。
ステップ4:送信時のトークン検証とリフレッシュ
async function sendEmailViaGraph(userId, mailBody) {
let tokens = await loadTokens(userId);
// トークン期限切れ確認(5分のマージン)
if (tokens.expires_at < Date.now() + 5 * 60 * 1000) {
tokens = await refreshAccessToken(tokens.refresh_token);
await saveTokens(userId, tokens);
}
const response = await fetch('https://graph.microsoft.com/v1.0/me/sendMail', {
method: 'POST',
headers: {
'Authorization': `Bearer ${tokens.access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ message: mailBody })
});
if (response.status === 401) {
// トークン無効化された場合は再認証を促す
throw new TokenExpiredError('OAuth再認証が必要です');
}
}
Kintone側の統合ポイント
プラグイン設定画面
プラグインの設定画面では、「Googleに接続」「Microsoftに接続」ボタンを表示します。クリックすると、新しいウィンドウでOAuth認証画面を開き、完了後に元の画面に戻ります。
// プラグイン設定画面のJavaScript
document.getElementById('connect-google').addEventListener('click', () => {
const authUrl = `https://api.your-plugin.com/oauth/google/start?subdomain=${kintone.getLoginUser().domain}`;
const popup = window.open(authUrl, 'oauth', 'width=500,height=600');
// コールバック完了を待つ
window.addEventListener('message', (e) => {
if (e.data.type === 'oauth-complete') {
popup.close();
refreshConnectionList();
}
});
});
サブドメイン単位でのスコープ
複数サブドメインで運用する場合、OAuth接続はサブドメイン単位で分離します。sales.kintone.comで接続したGmailアカウントが、ops.kintone.comからは使えないようにする設計です。
// 接続情報保存時にサブドメインを記録
async function saveOAuthConnection(subdomain, userEmail, tokens) {
await kintone.api('/k/v1/record', 'POST', {
app: OAUTH_CONNECTIONS_APP_ID,
record: {
subdomain: { value: subdomain },
user_email: { value: userEmail },
encrypted_tokens: { value: encryptTokens(tokens) },
scope: { value: 'Mail.Send User.Read' }
}
});
}
// 送信時にサブドメインで絞り込み
async function loadOAuthConnection(subdomain, userEmail) {
const records = await kintone.api('/k/v1/records', 'GET', {
app: OAUTH_CONNECTIONS_APP_ID,
query: `subdomain = "${subdomain}" and user_email = "${userEmail}"`
});
return records.records[0];
}
移行時のトラブルシューティング
「管理者同意が必要」エラー
Microsoft Entra ID で、組織的に管理者同意を必要とするスコープ(例:Mail.Send.Shared)を使っている場合、一般ユーザーの認証時にエラーが出ます。解決策は、テナント管理者が事前に「組織全体に対する同意」を付与するか、個別ユーザーに対して同意を求めるフローに変更するかです。
「invalid_grant」エラー
refresh tokenが無効化された場合に発生します。ユーザーがパスワード変更・MFA再設定・セッション無効化のいずれかを行うとrefresh tokenも失効します。プラグイン側では、このエラーを検知して、ユーザーに再認証を促すUIを表示する実装が必要です。
「rate limit exceeded」エラー
Microsoft Graph APIにはレート制限があり(テナント単位で1秒あたり約100リクエスト)、大量送信時に遭遇します。429エラーを検知したら、Retry-Afterヘッダに従って待機し、リトライする実装にします。
Send As(委任送信)の実装
共有メールボックス(例:info@your-company.com)から個別ユーザーアカウント経由で送信する場合、Microsoft側でSend As権限の委任が必要です。
Exchange Admin Centerで設定:
- Receiving → Shared mailboxes から対象のメールボックスを選択
- 「メールボックスのアクセス許可」→ 「他のユーザーへのアクセス許可」
- Send Asを付与したいユーザーを追加
API側の実装:
// Graph API で Send As を使う場合
await fetch(`https://graph.microsoft.com/v1.0/users/${sharedMailboxId}/sendMail`, {
// users/{id}/sendMailで、委任されたユーザーの代わりに送信
headers: {
'Authorization': `Bearer ${delegatedUserToken}`, // 委任されたユーザーのトークン
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: {
// from は自動的に shared mailbox になる
toRecipients: [...]
}
})
});
まとめ
SMTP基本認証からOAuth 2.0への移行は、単なる認証方式の置き換えではなく、トークン管理・エラーハンドリング・スコープ設計の再構築を伴います。Node.js + Kintoneの実装パターンを押さえれば、3~5日で実装可能です。ただし、エンタープライズ環境では管理者同意・Send As委任・レート制限対応など、運用面の配慮が追加で必要になります。
Kinplug MailではこれらすべてをOAuth 2.0ネイティブで設計しており、実装済みで提供しています。既存プラグインからの移行支援も無料で行っています。