Salesforce JWTベアラーフロー完全ガイド:インテグレーションエンジニアのための実践解説

こんにちは、Salesforce インテグレーションエンジニアです。日々の業務では、Salesforce と外部システムをシームレスに連携させることが求められます。特に、サーバー間の連携において、ユーザーの操作を介さずに安全な認証・認可を実現することは非常に重要な課題です。今回は、この課題を解決するための強力なソリューションである OAuth 2.0 JWT (JSON Web Token) Bearer Flow について、インテグレーションの観点から深く掘り下げて解説します。


背景と応用シナリオ

従来の OAuth 2.0 フロー(例えば Authorization Code Flow)では、ユーザーがブラウザ上で Salesforce のログイン画面にリダイレクトされ、認証情報を入力し、アプリケーションへのアクセスを許可する、という一連の操作が必要でした。しかし、夜間バッチ処理で外部の ERP システムから Salesforce にデータを同期する場合や、CI/CD パイプラインが Salesforce にメタデータをデプロイする場合など、ユーザーが介在しないサーバー間通信のシナリオではこの方法は適用できません。

ここで登場するのが JWT (JSON Web Token - ジェイソン・ウェブ・トークン) を利用した認証フローです。JWT Bearer Flow は、クライアントアプリケーションが事前に発行された証明書(秘密鍵)を使って自己署名した JWT を Salesforce に提示することで、ユーザーのIDとパスワードを直接やり取りすることなく、アクセストークンを取得できる仕組みです。これにより、以下のようなシナリオで安全かつ自動化されたインテグレーションが可能になります。

主な応用シナリオ:

  • サーバー間データ同期: 外部のデータウェアハウスや基幹システムが、バックエンドで Salesforce のデータを定期的に更新・取得する。
  • CI/CD パイプライン連携: Jenkins, GitHub Actions, Azure DevOps などのツールが、自動で Salesforce 環境へのデプロイや Apex テストを実行する。
  • カスタムAPIゲートウェイ: 企業独自の API ゲートウェイが、Salesforce をバックエンドサービスとして利用する際に、リクエスト元を認証する。
  • イベント駆動型アーキテクチャ: 外部システムで発生したイベント(例:新規顧客登録)をトリガーに、Salesforce Platform Events を発行したり、レコードを作成したりする。

これらのシナリオでは、認証情報を安全に管理し、人の手を介さずに API アクセスを確立することが成功の鍵となります。JWT Bearer Flow は、そのための理想的なソリューションと言えるでしょう。


原理説明

JWT Bearer Flow の仕組みを理解するためには、まず JWT そのものの構造を把握する必要があります。JWT は、ドット (`.`) で区切られた3つのパートから構成されるコンパクトで自己完結した文字列です。

[ヘッダー].[ペイロード].[署名]

1. ヘッダー (Header)

ヘッダーは、トークンのタイプ(通常は "JWT")と、署名の生成に使用されるアルゴリズム(例:`RS256` - RSA 署名と SHA-256)を指定する JSON オブジェクトです。この JSON は Base64Url エンコードされて JWT の最初のパートになります。

例:`{"alg":"RS256","typ":"JWT"}`

2. ペイロード (Payload)

ペイロードには、クレーム (Claims) と呼ばれる、エンティティ(通常はユーザー)や追加のデータに関する情報が含まれます。Salesforce の JWT Bearer Flow では、以下のクレームが必須または推奨されます。

  • `iss` (Issuer): 発行者。Salesforce の接続アプリケーションの Consumer Key (コンシューマキー) を指定します。
  • `sub` (Subject): 対象者。API アクセスを許可する Salesforce ユーザーのユーザー名を指定します。このユーザーとして API が実行されます。
  • `aud` (Audience): 受信者。トークンを発行する先の Authorization Server の URL を指定します。本番環境では `https://login.salesforce.com`、Sandbox 環境では `https://test.salesforce.com` となります。
  • `exp` (Expiration Time): 有効期限。トークンが無効になる時刻を UNIX タイムスタンプ(1970年1月1日からの秒数)で指定します。セキュリティのため、有効期間は数分程度の短い時間に設定することが強く推奨されます。

ペイロードも Base64Url エンコードされ、JWT の2番目のパートになります。

3. 署名 (Signature)

署名は、JWT の完全性と信頼性を保証する最も重要な部分です。以下の手順で生成されます。

  1. エンコードされたヘッダーとペイロードをドットで連結します。 (`encodedHeader + "." + encodedPayload`)
  2. その連結された文字列を、ヘッダーで指定されたアルゴリズム(`RS256`)と、クライアントアプリケーションが保持する秘密鍵 (Private Key) を使って署名します。
  3. 生成された署名を Base64Url エンコードします。

Salesforce 側では、接続アプリケーションにアップロードされた公開鍵 (Public Key) を使ってこの署名を検証します。検証に成功すれば、その JWT が信頼できる発行者(秘密鍵を持つクライアント)によって作成され、途中で改ざんされていないことが証明されます。これにより、Salesforce は安心してアクセストークンを発行できるのです。


示例代码

JWT の生成は、多くのプログラミング言語でライブラリが提供されていますが、ここでは Salesforce の公式ドキュメントに記載されている Apex を使って JWT を生成し、署名する例を示します。このコードは、例えば Salesforce 組織間で API 連携を行うようなシナリオで利用できます。

この例では、Salesforce の「証明書と鍵の管理」で作成した自己署名証明書(`jwt_cert`)を使用して JWT に署名します。

// JWT のクレームセットを構築します。
// iss: 接続アプリケーションのコンシューマキー
// sub: 実行ユーザーのユーザー名
// aud: トークンエンドポイントの URL
// exp: トークンの有効期限(ここでは現在から3分後)
String iss = '3MVG9A2kN3Bn17hs..somelongconsumerkey...';
String sub = 'user@example.com';
String aud = 'https://login.salesforce.com';
Long exp = System.currentTimeMillis() / 1000 + 180; // 3分間有効

// Auth.JWT クラスを使用して JWT を初期化します。
Auth.JWT jwt = new Auth.JWT();
jwt.setIss(iss);
jwt.setSub(sub);
jwt.setAud(aud);
jwt.setAdditionalClaims(new Map<String, Object>{'exp' => exp});

// 「証明書と鍵の管理」で作成した証明書の一意の名前を指定します。
// この証明書に関連付けられた秘密鍵が署名に使用されます。
String certificateName = 'jwt_cert';

// Auth.JWS クラスを使用して、指定した証明書で JWT に署名します。
// コンストラクタは JWT オブジェクトと証明書の名前を引数に取ります。
Auth.JWS jws = new Auth.JWS(jwt, certificateName);

// computeMac() メソッドは、ヘッダーとクレームセットを base64url エンコードし、
// それらを連結して、指定された証明書の秘密鍵で署名します。
// 最終的に、完全な JWS 形式のトークン文字列を返します。
String token = jws.getCompactSerialization();

System.debug('Generated JWT: ' + token);

// この生成された `token` を、HTTP POST リクエストの `assertion` パラメータとして
// Salesforce のトークンエンドポイントに送信し、アクセストークンを取得します。
// 例:
// POST /services/oauth2/token
// grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
// &assertion=eyJhbGciOiJSUzI1NiJ9.ey...

この Apex コードは、`Auth.JWT` クラスでクレームを設定し、`Auth.JWS` クラスで Salesforce プラットフォームに安全に保管されている証明書の秘密鍵を使って署名するプロセスを明確に示しています。外部システムで JWT を生成する場合も、同様のロジックを各言語のライブラリで実装することになります。


注意事項

JWT Bearer Flow を実装・運用する際には、インテグレーションエンジニアとして以下の点に注意する必要があります。

1. 接続アプリケーション (Connected App) の設定

  • デジタル署名の使用: 接続アプリケーションの設定で、「デジタル署名を使用」にチェックを入れ、クライアントが署名に用いる秘密鍵に対応する公開証明書(.crt ファイル)をアップロードする必要があります。
  • OAuth スコープ: 連携に必要な最小限のスコープ(例:「API を使用してユーザーデータを管理 (api)」「いつでも要求を実行 (refresh_token, offline_access)」)を選択します。
  • ポリシー設定: 「OAuth ポリシー」で、「許可されているユーザー」を「管理者が承認したユーザは事前承認済み」に設定することを強く推奨します。これにより、予期せぬユーザーによるアクセスを防ぎます。

2. ユーザーの事前承認 (Pre-authorization)

このフローの重要なステップが、管理者がユーザーに対してこの接続アプリケーションの利用を「事前承認」することです。これを怠ると、有効な JWT を送信しても `{"error":"invalid_grant","error_description":"user hasn't approved this consumer"}` というエラーが返されます。事前承認は、対象ユーザーが割り当てられているプロファイルまたは権限セットの「割り当てられた接続アプリケーション」セクションで行います。

3. 証明書と秘密鍵の管理

JWT の署名に使用する秘密鍵は、システムの命綱です。絶対に漏洩させてはいけません。コードリポジトリに直接含めるようなことはせず、Azure Key Vault, AWS KMS, HashiCorp Vault などのセキュアなキーストアサービスで管理してください。また、証明書には有効期限があるため、証明書の期限切れによるインテグレーションの停止を防ぐため、証明書のローテーション計画を立て、自動化しておくことがベストプラクティスです。

4. クロック同期 (Clock Skew)

JWT の `exp` クレームは時刻に依存するため、JWT を生成するクライアントサーバーと Salesforce のサーバー間で時刻が大きくずれている(クロックスキュー)と、トークンが有効期限切れ、またはまだ有効になっていないと判断され、認証に失敗する可能性があります。クライアントサーバーの時刻が NTP (Network Time Protocol) などで正確に同期されていることを確認してください。

5. エラーハンドリング

トークン取得リクエストが失敗した場合、Salesforce はエラーコードと説明を返します。`invalid_grant`(署名が不正、ユーザーが未承認など)、`invalid_client_id`(`iss` が不正)などの一般的なエラーを適切に処理し、ログに記録する仕組みを実装することが、問題発生時の迅速なトラブルシューティングに繋がります。


まとめとベストプラクティス

OAuth 2.0 JWT Bearer Flow は、ユーザーの介入を必要としないサーバー間インテグレーションにおいて、非常にセキュアで信頼性の高い認証・認可メカニズムを提供します。インテグレーションエンジニアとしてこのフローをマスターすることは、堅牢でスケーラブルな Salesforce 連携ソリューションを構築する上で不可欠です。

ベストプラクティス:

  • 専用のインテグレーションユーザーを使用する: API 連携のためだけに、最小限の権限(CRUD/FLS)を持つ専用の Salesforce ユーザーを作成します。これにより、万が一認証情報が漏洩した際の影響範囲を最小限に抑えることができます(最小権限の原則)。
  • 秘密鍵を厳重に管理する: 繰り返しになりますが、秘密鍵の管理は最も重要です。セキュアなキーストアを利用し、鍵へのアクセスを厳しく制限してください。
  • JWT の有効期限を短く設定する: `exp` クレームで指定する有効期限は、トークン取得に必要な時間プラスαの短い時間(例:3分〜5分)に設定し、リプレイ攻撃のリスクを低減します。
  • 監査とモニタリング: Salesforce の「ログイン履歴」やイベントモニタリングを活用して、JWT Bearer Flow による認証イベントを定期的に監視し、不審なアクティビティがないかを確認します。
  • 証明書のローテーションを計画・自動化する: インテグレーションが突然停止することを避けるため、証明書の有効期限を監視し、期限が切れる前に新しい証明書に切り替えるプロセスを確立しておきましょう。

これらの原理と注意点を理解し、ベストプラクティスに従うことで、Salesforce と外部システム間の安全で効率的な自動連携を実現することができるでしょう。

コメント