Salesforce 指定ログイン情報:安全なAPI連携とコールアウト実装のベストプラクティス

背景と利用シーン

Salesforce 統合エンジニア (Salesforce Integration Engineer) として、私は日々、Salesforce と外部システムを安全かつ効率的に接続するという課題に取り組んでいます。過去のプロジェクトでは、外部 API のエンドポイント URL や認証情報をカスタムメタデータ、カスタム設定、あるいは最悪の場合 Apex コード内に直接ハードコーディングするという手法が散見されました。しかし、このアプローチには看過できない多くの問題が潜んでいます。

・セキュリティリスク:API キーやパスワードなどの機密情報が、コードやメタデータ内に平文または簡易な難読化で保存されるため、組織のセキュリティポリシーに違反し、情報漏洩のリスクを高めます。
・メンテナンスの煩雑さ:エンドポイントの URL や認証情報が変更された場合、コードやメタデータの複数箇所を修正し、再デプロイする必要が生じます。特に、Sandbox、UAT (受け入れテスト環境)、本番環境で異なるエンドポイントを管理するのは非常に手間がかかり、ヒューマンエラーの原因となります。
・柔軟性の欠如:認証方式の変更(例:Basic 認証から OAuth 2.0 へ)に対応するには、大幅なコード修正が必要となり、迅速なビジネス要件への対応が困難になります。

これらの課題を解決するために Salesforce が提供する強力な機能が Named Credentials (指定ログイン情報) です。Named Credentials は、外部サービスのコールアウトエンドポイントの URL と必要な認証情報を一元的に管理するための設定です。これにより、開発者は Apex コードから認証情報や具体的なエンドポイント URL を完全に分離し、より安全で保守性の高いインテグレーションを構築することが可能になります。

具体的な利用シーンとしては、以下のようなケースが挙げられます。

利用シーンの例:

  • Google Maps API や Stripe 決済 API といった、外部の RESTful API との連携
  • 社内の基幹システム (ERP) やデータウェアハウス (DWH) とのデータ同期
  • SOAP Web サービスを利用したレガシーシステムとの接続
  • 開発、テスト、本番環境ごとに異なるエンドポイントや認証情報を、コードを変更することなく切り替える運用

原理説明

Named Credentials の中核的な思想は、「関心の分離」です。つまり、「どこに接続するか(エンドポイント)」と「どのように認証するか(認証情報)」という設定を、それを「どのように利用するか(ビジネスロジック)」という Apex コードから切り離すことにあります。

Named Credential を設定すると、Salesforce は指定された情報に基づき、外部システムへの認証プロセス(例えば OAuth 2.0 のトークン取得・更新など)を自動的に管理します。開発者は Apex コード内で、設定した Named Credential の名前を特殊な `callout:` プレフィックスを付けて参照するだけで、認証済みの HTTP リクエストを簡単に作成できます。

主要な構成要素

Named Credentials は、主に以下の要素で構成されます。

1. Name (名前) と URL

・Name (名前): Apex コードや他の宣言的ツールから参照される一意の API 参照名です。
・URL: 接続先となる外部サービスのベース URL を指定します。(例: `https://api.example.com`)Apex コード側では、このベース URL に続く相対パス(例: `/v1/accounts`)を指定するだけで済みます。

2. Identity Type (ID 種別)

・Named Principal (指名ユーザプリンシパル): 組織内のすべてのユーザが、単一の認証情報セットを使用して外部システムにアクセスする場合に選択します。サーバー間連携で最も一般的に使用されるタイプです。
・Per User (ユーザごと): Salesforce ユーザが、各自の外部システムアカウント情報を使用して認証する場合に選択します。例えば、「各営業担当者が自身の Google アカウントで Google Drive API にアクセスする」といったシナリオで利用します。

3. Authentication Protocol (認証プロトコル)

Named Credentials は、様々な認証プロトコルをサポートしています。

・No Authentication: 認証が不要な公開 API 向けです。
・Password Authentication: ユーザ名とパスワードを使用する Basic 認証です。
・OAuth 2.0: 現在の API 連携における標準的な認証方式です。Salesforce 内で Auth. Provider (認証プロバイダ) を別途設定し、Named Credential と組み合わせることで、アクセストークンの取得やリフレッシュといった複雑なフローを Salesforce が自動的に処理してくれます。これは非常に強力な機能です。
・JWT (JSON Web Token): サーバー間で安全に情報を交換するための認証方式です。
・JWT Token Exchange: ある JWT を別の JWT に交換することで、サービス間の信頼関係を確立する高度な認証フローです。

4. External Credentials (外部ログイン情報)

近年のアップデートで導入された External Credentials (外部ログイン情報) は、Named Credentials の機能をさらに強化します。External Credentials は、認証プロトコルと権限のセットを定義する再利用可能なコンポーネントです。これにより、「認証方法」と「エンドポイント」を完全に分離できます。例えば、同じ OAuth 2.0 認証情報を使用する複数の API エンドポイント(例:`api.service.com/users` と `api.service.com/orders`)がある場合、一つの External Credential を作成し、それを複数の Named Credentials から参照することで、管理をさらに効率化できます。


サンプルコード

ここでは、`My_API_Service` という名前の Named Credential を使用して、外部の REST API からデータを取得する Apex コードの例を示します。このコードは Salesforce の公式ドキュメントに基づいています。

前提: 事前に、`My_API_Service` という名前の Named Credential が設定画面で作成されており、URL と認証情報が正しく構成されているものとします。URL は `https://api.example.com` と設定されていると仮定します。

// public class ApiCalloutController {

    @AuraEnabled(cacheable=true)
    public static String getApiServiceData() {
        // 1. HttpRequest オブジェクトのインスタンスを作成します。
        // これが、外部サービスに送信されるリクエストの本体となります。
        HttpRequest req = new HttpRequest();

        // 2. setEndpoint メソッドでリクエストの送信先を指定します。
        // 'callout:' プレフィックスは、Salesforce にこれが Named Credential を使用した
        // コールアウトであることを伝えます。
        // 'My_API_Service' は設定した Named Credential の API 参照名です。
        // '/api/v1/data' は、Named Credential の URL に追加される相対パスです。
        // 最終的なエンドポイントは 'https://api.example.com/api/v1/data' となります。
        req.setEndpoint('callout:My_API_Service/api/v1/data');

        // 3. HTTP メソッドを 'GET' に設定します。
        // データの取得を目的とするため、GET を使用します。
        req.setMethod('GET');

        // 4. Http オブジェクトのインスタンスを作成します。
        // このオブジェクトが、実際のリクエスト送信とレスポンス受信を処理します。
        Http http = new Http();
        HttpResponse res = null;

        try {
            // 5. http.send(req) を呼び出して、リクエストを送信し、レスポンスを待ちます。
            // Salesforce はこの時点で、Named Credential の設定に基づき、
            // 必要な認証ヘッダー(例: 'Authorization: Bearer [ACCESS_TOKEN]')を
            // 自動的にリクエストに追加します。開発者は認証の詳細を意識する必要がありません。
            res = http.send(req);

            // 6. レスポンスのステータスコードを確認します。
            // 200 は成功を意味します。
            if (res.getStatusCode() == 200) {
                // レスポンスボディを文字列として取得し、デバッグログに出力します。
                System.debug('Response Body: ' + res.getBody());
                return res.getBody();
            } else {
                // 成功以外のステータスコードの場合、エラーとして処理します。
                System.debug('Unexpected status code: ' + res.getStatusCode() + ' ' + res.getStatus());
                // エラーハンドリングのロジックをここに追加します。
                return 'Error: ' + res.getStatus();
            }
        } catch(System.CalloutException e) {
            // 7. ネットワークエラーやタイムアウトなど、コールアウト中に発生した例外をキャッチします。
            // 堅牢な統合のためには、例外処理が不可欠です。
            System.debug('Callout error: '+ e);
            return 'Callout Error: ' + e.getMessage();
        }
    }
// }

このコードの最大の利点は、認証情報やエンドポイントの URL が一切含まれていない点です。これにより、コードは特定の環境や認証情報に依存せず、非常に再利用性が高く、安全になります。


注意事項

Named Credentials を利用する際には、以下の点に注意する必要があります。

権限 (Permissions)

・管理者権限: Named Credentials および External Credentials を作成・編集するには、「アプリケーションのカスタマイズ」権限が必要です。
・ユーザ権限: Identity Type が `Per User` の場合、ユーザが外部サービスへの認証を開始できるように、プロファイルまたは権限セットを通じて、関連する External Credential Principal へのアクセスを許可する必要があります。

API 制限 (API Limits)

Named Credentials を使用しても、Salesforce のガバナ制限が免除されるわけではありません。Apex トランザクションあたりのコールアウト回数(同期 Apex では 100 回)、合計コールアウト時間(120 秒)、リクエスト/レスポンスのサイズ(ヒープサイズ制限に依存)などの制限は引き続き適用されます。設計段階でこれらの制限を十分に考慮する必要があります。

エラー処理 (Error Handling)

サンプルコードで示したように、API コールアウトは常に失敗する可能性があります。ネットワークの問題、外部サービスのダウン、認証情報の失効など、原因は様々です。したがって、すべての `http.send()` 呼び出しは `try-catch` ブロックで囲み、`System.CalloutException` を適切に処理することが不可欠です。また、HTTP レスポンスのステータスコードを必ず確認し、`200 OK` 以外のコード(`401 Unauthorized`、`404 Not Found`、`500 Internal Server Error` など)に対する処理ロジックを実装してください。

テスト (Testing)

Named Credentials を使用した Apex コードの単体テストを作成する際には、実際に外部へのコールアウトを行ってはなりません。テストの実行が外部要因に依存し、不安定になるためです。Salesforce が提供する `HttpCalloutMock` インターフェースを実装したテストクラスを使用し、コールアウトをモック(模擬)化する必要があります。これにより、様々なレスポンス(成功、エラーなど)をシミュレートし、コードの堅牢性を検証できます。

@isTest
private class ApiCalloutControllerTest {
    @isTest
    static void testGetApiServiceDataSuccess() {
        // モック用のレスポンスを作成します。
        MockHttpResponse mock = new MockHttpResponse(200, 'Success', '{"data":"mock_data"}');
        // Named Credential 'My_API_Service' とその相対パスへのコールアウトに対する
        // モックレスポンスを設定します。
        Test.setMock(HttpCalloutMock.class, 'callout:My_API_Service/api/v1/data', mock);

        Test.startTest();
        String result = ApiCalloutController.getApiServiceData();
        Test.stopTest();

        // 結果がモックで設定したボディと一致することを確認します。
        System.assertEquals('{"data":"mock_data"}', result);
    }
}
// ⚠️ この MockHttpResponse クラスは例であり、実際には HttpCalloutMock を実装する
// カスタムクラスを作成する必要があります。

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

Salesforce Named Credentials は、外部システムとのインテグレーションを構築する上で、現代の Salesforce プラットフォームに不可欠な機能です。統合エンジニアとして、私はこの機能がもたらす価値を強く推奨します。

主要な利点:

  • セキュリティの強化:認証情報をコードから分離し、Salesforce のセキュアなストレージで暗号化して管理します。
  • 保守性の向上:エンドポイントや認証情報の変更が設定画面のみで完結し、コードの変更やデプロイが不要になります。
  • コードの簡素化:認証ロジック(特に OAuth 2.0 のトークン管理)を Salesforce に任せることで、Apex コードがシンプルかつクリーンになります。

ベストプラクティス:

  1. 常に Named Credentials を使用する:全ての HTTP/REST/SOAP コールアウトには、ハードコーディングを避け、Named Credentials を利用してください。
  2. External Credentials を活用する:認証ロジックを再利用するために、External Credentials を積極的に活用し、Named Credentials と組み合わせることを検討してください。
  3. 適切な認証プロトコルを選択する:可能な限り、Basic 認証よりもセキュアな OAuth 2.0 などのモダンなプロトコルを選択してください。
  4. 堅牢なエラーハンドリングを実装する:全てのコールアウトに対して、例外処理とステータスコードのチェックを徹底してください。
  5. `HttpCalloutMock` で網羅的なテストを作成する:テストカバレッジを確保するだけでなく、成功・失敗両方のシナリオをシミュレートし、コードの品質を高めてください。
  6. 環境ごとの設定を計画する:開発、テスト、本番の各環境で異なる Named Credentials を利用する運用を前提に、デプロイ戦略を立ててください。

Named Credentials を正しく理解し活用することで、私たちはより安全で、スケーラブルかつ保守性の高いインテグレーションソリューションを迅速に提供することができます。これは、Salesforce 統合エンジニアにとって必須のスキルセットと言えるでしょう。

コメント