運営: Edamame Inc. · 東京・マニラ · Kintone運用2019年より
ジャーナル

Kintoneメールプラグイン OAuth 2.0移行の技術ガイド — Node.js実装例

2026年4月18日 · 読了時間 約9分 · Tom Arai · Edamame Inc. 代表

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ステップでメール送信が実現されます:

  1. 認可コード取得:ユーザーをGoogle/Microsoftの認証画面にリダイレクト、認可コードを取得
  2. トークン交換:認可コードをaccess tokenとrefresh tokenに交換
  3. トークン保存:暗号化してKintoneアプリまたは自社DBに保存
  4. API呼び出し:access tokenを使ってGmail API / Microsoft Graph API経由で送信。期限切れ時はrefresh tokenで自動更新

ステップ1:OAuth アプリ登録

Google Cloud側の設定

  1. Google Cloud Console(console.cloud.google.com)にアクセス
  2. プロジェクトを作成(例:kinplug-mail-production)
  3. 「APIとサービス」→「認証情報」で、OAuth 2.0 クライアントIDを作成
  4. リダイレクトURIに、プラグインのコールバックURL(例:https://api.your-plugin.com/oauth/google/callback)を登録
  5. スコープを「https://www.googleapis.com/auth/gmail.send」に限定(最小権限の原則)
  6. Gmail APIを有効化

Microsoft Entra ID側の設定

  1. Azure Portal(portal.azure.com)にアクセス
  2. Entra ID → アプリ登録 → 新規登録
  3. リダイレクトURIに、プラグインのコールバックURLを登録
  4. 「APIのアクセス許可」で Microsoft Graph の Mail.SendUser.Readoffline_access を追加
  5. 委任されたアクセス許可を選択(管理者同意が必要な場合あり)
  6. クライアントシークレットを発行(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で設定:

  1. Receiving → Shared mailboxes から対象のメールボックスを選択
  2. 「メールボックスのアクセス許可」→ 「他のユーザーへのアクセス許可」
  3. 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ネイティブで設計しており、実装済みで提供しています。既存プラグインからの移行支援も無料で行っています。

関連記事

次のステップ

kinplug を14日間無料で試す

クレジットカード不要。GoogleかMicrosoftでサインインして、Kintoneサブドメインを入力。90秒で稼働開始します。

無料で始める プラグインを見る
はじめる

14日間、全機能、
クレジットカード不要。

GoogleかMicrosoftでサインイン、Kintoneサブドメインを入力、プラグインをインストール。90秒で稼働開始。