Salesforce JWTベアラーフロー:安全なサーバー間API連携ガイド

背景と応用シナリオ

現代のエンタープライズシステムにおいて、システム間のデータ連携は不可欠です。特にSalesforceは、顧客関係管理 (CRM) の中心として、基幹システム、データウェアハウス、外部サービスなど、様々なシステムと連携するハブとなることが多くあります。これらの連携を実装する際、最も重要な課題の一つが「認証」です。

ユーザーが介在するWebアプリケーションなどでは、OAuth 2.0 Authorization Code Flow のように、ユーザーにログイン画面を提示して認証と認可を得るフローが一般的です。しかし、夜間バッチ処理やサーバー上で実行される自動化プロセスなど、ユーザーが操作を行わない「サーバー間連携 (Server-to-Server Integration)」のシナリオでは、この方法は適用できません。従来はユーザー名とパスワードを直接APIリクエストに含める方法もありましたが、セキュリティリスクが非常に高く、Salesforceでは推奨されていません。

このような課題を解決するのが、OAuth 2.0 JWT Bearer Flow です。JWTは JSON Web Token (JSON Webトークン) の略で、電子的に署名された自己完結型のトークンです。このフローを利用することで、クライアントアプリケーションは事前に発行された電子証明書(秘密鍵)を使って自身を認証し、ユーザーのパスワードを扱うことなく、安全にSalesforce APIへのアクセス権(アクセストークン)を取得できます。これにより、以下のような応用シナリオが実現可能になります。

  • 夜間データ同期バッチ: オンプレミスのERPシステムからSalesforceへ、毎晩自動で販売データを同期する。
  • CI/CDパイプライン: JenkinsやGitHub ActionsなどのCI/CDツールが、デプロイメントの一環としてSalesforceのメタデータを自動で更新する。
  • イベント駆動型連携: 外部のメッセージングキュー(例:AWS SQS)から受け取ったメッセージをトリガーに、サーバーアプリケーションがSalesforceのレコードを更新する。

本記事では、Salesforce技術アーキテクトの視点から、このJWT Bearer Flowの原理、具体的な実装方法、そして運用上の注意点について詳しく解説します。


原理説明

JWT Bearer Flowの仕組みを理解するためには、まずJWT自体の構造と、Salesforceにおける認証フローの全体像を把握する必要があります。フローは大きく分けて「JWTの生成と署名」「アクセストークンの要求」「APIへのアクセス」の3ステップで構成されます。

1. JWTの構造

JWTは、ピリオド (`.`) で区切られた3つの部分から構成される文字列です。それぞれBase64URLエンコードされています。

Header (ヘッダー) + . + Payload (ペイロード) + . + Signature (署名)

  • Header: トークンの種類(`JWT`)と、署名に使用されるアルゴリズム(例:`RS256`)などのメタデータを含みます。Salesforceでは`RS256`(RSA署名とSHA-256)が必須です。
  • Payload: Claims (クレーム) と呼ばれる、認証に必要な情報を含みます。これには、誰がトークンを発行したか(発行者)、誰のためのトークンか(利用者)、有効期限などの情報が含まれます。
  • Signature: ヘッダーとペイロードを、指定されたアルゴリズムと秘密鍵を使って署名したものです。この署名により、トークンが改ざんされていないこと、そして正当な発行者によって作成されたことを検証できます。

2. Salesforceにおける認証フロー

クライアントアプリケーションがSalesforceからアクセストークンを取得するまでの流れは以下の通りです。

ステップ1: 事前準備

  1. クライアントは、自己署名証明書または認証局 (CA) が発行した証明書のペア(秘密鍵と公開鍵証明書)を生成します。
  2. Salesforce側でConnected App (接続アプリケーション) を作成します。「API (Enable OAuth Settings)」を有効にし、「Use digital signatures」にチェックを入れ、生成した公開鍵証明書をアップロードします。
  3. Connected Appのポリシーで、この連携を利用するユーザーのプロファイルや権限セットを事前に許可(Pre-authorize)しておきます。

ステップ2: JWTの生成とアクセストークンの要求

  1. クライアントアプリケーションは、必要なClaims(後述)を含むJWT PayloadをJSON形式で作成します。
  2. HeaderとPayloadを準備し、ステップ1で生成した秘密鍵を使ってRS256アルゴリズムで署名を生成します。
  3. 完成したJWTを、Salesforceのトークンエンドポイント (`https://login.salesforce.com/services/oauth2/token`) へPOSTリクエストで送信します。

ステップ3: Salesforceによる検証と応答

  1. Salesforceはリクエストを受け取ると、JWTの発行者 (`iss` Claim) から該当するConnected Appを特定します。
  2. Connected Appに登録されている公開鍵証明書を使い、JWTの署名を検証します。検証に成功すれば、リクエストが正当なクライアントから送られたことが証明されます。
  3. Payload内のClaims(有効期限、利用者など)が有効であることを確認します。
  4. 全ての検証が成功すると、SalesforceはAPIアクセスに必要なアクセストークンを返却します。

このフローにより、クライアントはパスワードを一切送信することなく、安全に認証を完了できます。秘密鍵はクライアント側で厳重に保管され、Salesforce側は対となる公開鍵のみで検証を行うため、非常にセキュアな仕組みと言えます。


サンプルコード

ここでは、Apexを使用してSalesforce自身から別のSalesforce組織へJWT Bearer Flowで認証する例を示します。このコードは、例えば組織間のデータ同期を行う場合に利用できます。コードの基盤はSalesforceの公式ドキュメントに基づいています。

まず、証明書をSalesforceの「証明書と鍵の管理」で作成し、その証明書を使用するConnected Appを接続先組織で設定しておく必要があります。

public class JWTBearerFlowExample {

    // 接続先組織のログインURLとConnected AppのConsumer Key
    private static final String TOKEN_ENDPOINT = 'https://login.salesforce.com/services/oauth2/token';
    private static final String CONSUMER_KEY = 'YOUR_CONNECTED_APP_CONSUMER_KEY';
    // 接続先組織で認証するユーザーのユーザー名
    private static final String USERNAME = 'user@example.com';
    // Salesforceの「証明書と鍵の管理」で作成した証明書の一意の名前
    private static final String CERT_UNIQUE_NAME = 'SelfSignedCertForJWT';

    public static String getAccessToken() {
        // Auth.JWT クラスを使用してJWTのクレームを設定
        Auth.JWT jwt = new Auth.JWT();
        // iss (issuer): 発行者。Connected AppのConsumer Keyを指定します。
        jwt.setIss(CONSUMER_KEY);
        // sub (subject): 認証するユーザーのユーザー名を指定します。
        jwt.setSub(USERNAME);
        // aud (audience): オーディエンス。本番環境の場合はlogin.salesforce.com、Sandboxの場合はtest.salesforce.comを指定します。
        jwt.setAud(TOKEN_ENDPOINT);
        // 追加のクレームをセットすることも可能です。
        // jwt.setAdditionalClaims(new Map<String, Object>{'claim_name' => 'claim_value'});

        // Auth.JWS クラスを使用して、JWTに署名します。
        // 第1引数にJWTオブジェクト、第2引数にSalesforceに保存されている証明書の一意の名前を指定します。
        // この処理により、Salesforceが管理する秘密鍵で署名が行われます。
        Auth.JWS jws = new Auth.JWS(jwt, CERT_UNIQUE_NAME);
        
        // JWSをコンパクトなシリアライゼーション形式(ヘッダー.ペイロード.署名)の文字列に変換します。
        String token = jws.getCompactSerialization();

        // トークンエンドポイントに送信するリクエストボディを構築します。
        // grant_typeには'urn:ietf:params:oauth:grant-type:jwt-bearer'を指定します。
        // assertionには署名済みのJWTトークンを指定します。
        String requestBody = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + token;

        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(TOKEN_ENDPOINT);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setBody(requestBody);

        try {
            HttpResponse res = http.send(req);
            if (res.getStatusCode() == 200) {
                // レスポンスが成功した場合、JSONをパースしてアクセストークンを取得
                Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
                System.debug('Access Token: ' + responseMap.get('access_token'));
                return (String) responseMap.get('access_token');
            } else {
                // エラー処理
                System.debug('Error response: ' + res.getBody());
                // エラー内容を適切にログに記録または例外をスローする
                return null;
            }
        } catch (Exception e) {
            System.debug('Exception during callout: ' + e.getMessage());
            return null;
        }
    }
}

注意事項

JWT Bearer Flowを安全かつ安定して運用するためには、いくつかの重要な点に注意する必要があります。

1. Connected Appの正しい設定

Connected Appの設定は、このフローの根幹をなします。以下の項目を正確に設定してください。

  • API (Enable OAuth Settings)の有効化: これを有効にしないとOAuthフロー自体が利用できません。
  • Use digital signaturesの有効化: JWTの署名検証に必須です。ここにクライアントの公開鍵証明書をアップロードします。
  • OAuth Scopes: クライアントが必要とする最小限のスコープ(例: `api`, `refresh_token`)を選択します。過剰な権限を与えない「最小権限の原則」を遵守してください。
  • Permitted Users Policy (許可されたユーザー): 「Admin approved users are pre-authorized (管理者が承認したユーザーは事前承認済み)」を選択し、関連するプロファイルや権限セットを明示的に追加します。これにより、ユーザーが個別にアプリを承認する手間が省け、サーバー間連携が可能になります。

2. 証明書と鍵の管理

署名に使用する秘密鍵は、システムの認証における最重要情報です。これが漏洩すると、第三者がシステムになりすましてSalesforceにアクセスできてしまいます。秘密鍵は、アクセスが厳しく制限された安全な場所に保管してください。Apexから利用する場合は、Salesforceプラットフォーム内の「証明書と鍵の管理」機能で作成・保管するのが最も安全です。外部システムの場合は、HashiCorp VaultやAWS KMSなどのシークレット管理サービスを利用することを強く推奨します。

3. JWT Claimsの正確な設定

Payloadに含まれるClaimsは、Salesforceによって厳密に検証されます。特に以下のClaimは必須であり、値が間違っていると認証に失敗します。

  • iss (Issuer): Connected AppのConsumer Key
  • sub (Subject): 連携を実行するユーザーのユーザー名
  • aud (Audience): 認証サーバーのURL。本番組織は `https://login.salesforce.com`、Sandboxは `https://test.salesforce.com` です。
  • exp (Expiration Time): トークンの有効期限。UNIX時間(1970年1月1日からの秒数)で指定します。セキュリティのため、有効期限は3分以内に設定する必要があります。また、サーバー間の時刻のずれを考慮し、発行時刻を少し過去に設定することが推奨される場合もあります。

4. エラーハンドリング

トークンリクエストが失敗すると、Salesforceはエラーコードを含むJSONレスポンスを返します。代表的なエラーとその原因を理解しておくことが、トラブルシューティングに役立ちます。

  • `invalid_grant` / `user hasn't approved this consumer`: ユーザーがConnected Appを事前承認していない。Connected Appのポリシー設定を確認してください。
  • `invalid_grant` / `invalid assertion`: JWTの署名が無効、またはClaimsの内容(`iss`, `aud`など)が間違っている。証明書やClaimsの設定を見直してください。
  • `invalid_client_id`: `iss` Claimに指定されたConsumer Keyが存在しない、または無効。

アプリケーション側では、これらのエラーを適切に捕捉し、リトライ処理や管理者への通知を行うロジックを実装することが重要です。


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

OAuth 2.0 JWT Bearer Flowは、ユーザーの介在を必要としないサーバー間連携において、パスワードを直接扱わずに済む、非常にセキュアで堅牢な認証方式です。正しく実装することで、Salesforceとの自動連携の安全性を飛躍的に高めることができます。

最後に、JWT Bearer Flowを実装・運用する上でのベストプラクティスをいくつか紹介します。

  1. Named Credentials (指定ログイン情報) の活用: Apexから外部のSalesforce組織へ接続する場合、トークン取得ロジックを自前で実装するのではなく、Named Credentials を利用することを強く推奨します。Named Credentialsで認証プロトコルとして「JWT Token Exchange」を選択すると、証明書や発行者、利用者などの設定を宣言的に行うだけで、Salesforceプラットフォームが裏側でアクセストークンの取得と管理(キャッシュやリフレッシュ)を自動的に行ってくれます。これにより、コードが簡潔になり、メンテナンス性も向上します。
  2. 連携専用ユーザーと権限セットの利用: API連携のためだけに専用のインテグレーションユーザーを作成し、そのユーザーには必要最小限のオブジェクトや項目へのアクセス権のみを付与した権限セットを割り当てます。これにより、万が一認証情報が漏洩した際の影響範囲を最小限に抑えることができます。
  3. 証明書の定期的な更新: セキュリティポリシーに従い、署名に使用する証明書を定期的に更新するプロセスを確立してください。証明書の有効期限切れは、システム連携の停止に直結します。
  4. ログと監視: 認証の成功・失敗を監視し、特に認証失敗が多発する場合にはアラートを発する仕組みを導入します。これにより、不正アクセスの試みや設定ミスを早期に検知できます。

これらのベストプラクティスを遵守することで、JWT Bearer Flowのメリットを最大限に引き出し、安全でスケーラブルなSalesforce連携基盤を構築することが可能になります。

コメント