PHPでのSaleForceのOAuth 2.0 JWT認証方法について

セールスフォース

開発担当の梅です。今回の記事では、PHPでのSaleForceのOAuth 2.0 JWT認証方法(SandBox環境で検証済み)をご紹介いたします。
Salesforceは世界シェアNo.1の営業支援・CRMツールとして、顧客管理、案件管理、見込み客管理、売上予測、レポートとダッシュボードなど様々な機能が利用でいます。Salesforceの情報を自社システムに同期したいという場合、どうしたらいいでしょうか?

rest-api

Salesforceはサーバ間のデータ交換でREST APIとSOAP APIを用意してありまして、今回の記事はそれらのWeb サービス APIを利用するためのOAuth 2.0 JWT認証方法を説明します。

目次はこちら

  • 1. OpenSSLで非公開鍵と自己署名デジタル証明書を作成する
  • 2. Salesforce接続アプリケーションを作成する
  • 3. PHPでJWTを作成する
  • 4. PHPでアクセストークンを取得する
  • 5. PHPでSaleForceから情報を取得する

 OpenSSLで非公開鍵と自己署名デジタル証明書を作成する

この証明書は、OAuth 2.0認証で、Salesforceに渡したJWTが有効かどうかを検証するためのものです。基本的にはSalesforceの開発者ガイド「非公開鍵と自己署名デジタル証明書の作成」を参照すれば問題なく作成できます。

作業手順1.非公開鍵を生成し、server.key というファイルに保存します。

openssl genrsa -des3 -passout pass:任意のパスワード -out server.pass.key 2048
openssl rsa -passin pass:任意のパスワード -in server.pass.key -out server.key

作業手順2.server.key ファイルを使用して、証明書署名要求を生成します。server.csr というファイルに証明書署名要求を保存します。

openssl req -new -key server.key -out server.csr
Country Name (2 letter code) [XX]:JP <- 会社が所在する国を示す2文字のコード。日本の場合、値はJPです。
State or Province Name (full name) []:Tokyo <- 都道府県
Locality Name (eg, city) [Default City]:Taito <- 市区町村
Organization Name (eg, company) [Default Company Ltd]:tatsuno joho system <- 会社名(任意)
Organizational Unit Name (eg, section) []: <- 部署名(任意)
Common Name (eg, your name or your server's hostname) []:lms.quizgenerator.net <- 一般名(任意)
Email Address []:k.bai@tatsuno-system.co.jp <- メールアドレス(任意)
A challenge password []: <- 不要
An optional company name []: <- 不要

作業手順3.server.key および server.csr ファイルから自己署名デジタル証明書を生成します。server.crt というファイルに証明書を保存します。

openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

コマンドを実行したパスの直下に、次のように4つのファイルが生成されますが、その中に必要となるのは「server.key」と「server.crt」です。

    server.pass.key
    server.key
    server.csr
    server.crt

    プライマリキー(署名用)「server.key」の中身はこんな感じです。

    -----BEGIN RSA PRIVATE KEY-----
    MIIEpAIBAAKCAQEA09nuSOujeZ0AyR8gZ0jg0MOftWUD64IVJcIrULNCdv5/ZQ8H
    lvf6lOCs/77FWej07XZaNsBTpllX+yZSs3iFY+VZN/xW+6/e6+vARfPUL6IjnRIp
    drjXIj811qkvfeWXnZmJQgI7EurhwBgg2Oy2rcZFRL+KJGLhgD9cTkGr06dCIU/B
    
    ...省略...
    
    QYimkGrlPOzEcFEg/Md3n8UwZ7CSHIG0+xPp/8vnpqNmDPOCw7iEI4iJIyBuTkH4
    wzKOvJECgYAJ3ORif2l5LT4KCi5UZL27myUGqyDyt5VPa6H/RB3TChKtpnVcBBDF
    dCnvFqMv+AC3F2xFpcr3HK2l1H8Ya6yy7QMP9fqgHPWKrkpUSZcT/QvZniCreGsS
    7jbhD4R4VOPdu7I6cSWW+lA8mWTwK1Eym0FBl6d8i9q4GrxxcRTv2Q==
    -----END RSA PRIVATE KEY-----
    

    自己署名デジタル証明書(検証用)「server.crt」の中身はこんな感じです。

    -----BEGIN CERTIFICATE-----
    MIIDpDCCAowCCQDYJxgXlQ+UtjANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMC
    SlAxDjAMBgNVBAgMBVRva3lvMQ4wDAYDVQQHDAVUYWl0bzEcMBoGA1UECgwTdGF0
    c3VubyBqb2hvIHN5c3RlbTEbMBkGA1UEAwwSYmFpLnF1aXpnZW5kZXYubmV0MSkw
    
    ...省略...
    
    wpGlYCfAynDVPdE87rI5mitvPFfTAGn+M+kYTaVW4HqVvDMLX+X/3ID0aM5i+k3V
    8e2s/GvubOtcYty1nLf4IMtz6ikrfDfiHEmu2nqb+wrsKyvuTwVLakgdKKRYZgQv
    EIoWUaXAfGDGYyOOsrN6XWwpqp0Yzh51+v1nK+PDBd/fIufge6BDA5EDH3wAJjXu
    PeTR5HfZoXN5bdBk4KC4ueAGdw8ogbQJ
    -----END CERTIFICATE-----
    

     Salesforce接続アプリケーションを作成する

    作業手順1.Salesforceにログインしたら、右上のメニューから「設定」画面を開きます。

    PHP

    作業手順2.設定画面の左側にあるメニューから「アプリケーションマネージャ」を開き、「新規接続アプリケーション」をクリックします。

    php

    作業手順3.接続アプリケーションを設定します。(設定完了後は「保存」を忘れないように)

    • 接続アプリケーション名(任意の内容)
    • API 参照名(分かりやすい説明を入れましょう)
    • 取引先責任者 メール
    • OAuth 設定の有効化(必ずチェックを入れてください)
    • コールバック URL(利用しませんが、必須入力項目ですから、適当に何かを入力します。)
    • デジタル署名を使用(必ずチェックを入れてください)
    • 自己署名デジタル証明書(最初の手順で作った「server.crt」を選んでください)
    • 選択した OAuth 範囲
      ※「フルアクセス (full)」だとアクセストークはうまく取れませんでした。

      • Web 経由のデータへのアクセスを提供 (web)
      • データへのアクセスと管理 (api)
      • ユーザに代わっていつでも要求を実行 (refresh_token, offline_access)

    上記以外の設定内容はデフォルトのままで問題ございません。

    php

    作業手順4.「manage」ボタンを押して、管理画面を開きます。(ここはなぜか英語表記になっています。)

    php

    作業手順5.「ポリシーを編集」ボタンを押して、ポリシー編集画面を開きます。

    php

    作業手順6.「OAuth ポリシー」を次のように設定します。その他の設定はデフォルトのままで問題ございません。設定が終わったら「保存」ボタンを忘れずに押してください。

    「OAuth ポリシー」を「管理者が承認したユーザは事前承認済み」に設定したら、アプリケーションの管理画面では「プロファイル」が設定可能になります。

    php

    作業手順7.アプリケーションの管理画面にある「プロファイル」設定エリアで「プロフィールを管理する」ボタンを押して、アプリケーションのプロフィールを割当てます。

    php

    REST API経由でSaleforceからデータ取得・更新したいので、ここはフル権限を持った「System Admin」を割当てました。

    必要に応じて、適切なプロフィールを割当てましょう。

    php

    プロフィールの設定内容を保存したら、自動的にアプリケーションの管理画面に戻り、設定結果が確認できます。

    php

     PHPでJWTを作成する

    SaleForceの開発者ガイドで公開した作成方法はこちらです。

    ※ドキュメントが古いか、翻訳ミスかはわかりませんが、誤ったところが多くて参考になれませんでした。

    JWT(JSON Web Token)構造は大きく分けて、3つの部分になりまして、それぞれ「.」で区切られます。

    • ヘッダー(Header)
    • ペイロード(Payload)
    • 署名(Sinature)

    JWT(JSON Web Token)の文字列の全体を見ると、次のようになります。

    {ヘッダー>}.{ペイロード}.{署名}
    

    次のように、PHPでSaleForce用のOAuth 2.0 JWTが作成できます。

    ここでopenssl_pkey_get_private関数に渡すファイルのパスにハマってしまいました。

    ファイルパスは必ず「file://相対パス、または絶対パス」のように書いてください。

    function base64UrlEncode($data)
    {
        return str_replace('=', '', strtr(base64_encode($data), '+/', '-_'));
    }
    
    $header = base64UrlEncode(json_encode([
        'alg' => 'RS256',
    ]));
    
    /**
     * audについて
     * 本番: https://login.salesforce.com
     * Sandbox: https://test.login.salesforce.com
     * スクラッチ組織: https://test.saleforce.com
     */
    $payload = base64UrlEncode(json_encode([
        //iss間違ったらエラーメッセージ「接続アプリケーションは存在しない」が返ってきます。
        'iss' => '前の手順で作成したSaleForce接続アプリケーションのコンシューマ鍵',
        'aud' => 'https://test.salesforce.com',
        //sub間違ったらエラーメッセージ「無効なユーザ」が返ってきます。
        'sub' => 'SaleForceユーザのログインID',
        //ここの期限切れ時間はかなり適当です。exp間違ったらエラーメッセージ「トークが期限切れ」が返ってきます。
        'exp' => time() + 3 * 60,
    ]));
    
    /**
     * 署名について
     * 前の手順で作成した「server.key」を利用してJWTのヘッダーとペイロードを書名します。
     */
    $signature = null;
    $privateKey = openssl_pkey_get_private('file://server.key');
    openssl_sign($header . '.' . $payload, $signature, $privateKey, OPENSSL_ALGO_SHA256);
    openssl_free_key($privateKey);
    
    $signature = base64UrlEncode($signature);
    $jwt = $header . '.' . $payload . '.' . $signature;
    

     PHPでアクセストークンを取得する(cUrl利用)

    次のように、PHPでSaleForce側にアクセスして、アクセストークを発行して頂きます。

    $post = [
        'grant_type'    => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion'     => $jwt, //前の手順で作ったJWTの文字列
    ];
    
    /**
     * アクセストークンを取得するAPIについて
     * 本番: https://login.salesforce.com/services/oauth2/token
     * Sandbox: https://test.login.salesforce.com/services/oauth2/token
     * スクラッチ組織: https://test.saleforce.com/services/oauth2/token
     */
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://test.salesforce.com/services/oauth2/token');
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 7);
    curl_setopt($ch, CURLOPT_TIMEOUT, 7);
    $response = curl_exec($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    if($httpcode == 200) {
        $json = json_decode($response, true);
        if(isset($json['access_token'])) {
            $access_token = $json['access_token'];
            //do something.
        }
    } else {
        var_dump($httpcode);
        var_dump($response);
    }
    curl_close($ch);
    

    問合せ結果として、次のようなjsonデータが返ってきます。

    {
        "access_token" => "***********************************",
        "scope" => "web id api",
        "instance_url" => "https://SaleForce環境名.salesforce.com",
        "id" => "https://test.salesforce.com/id/*****/*****",
        "token_type" => "Bearer"
    }
    

     PHPでSaleForceから情報を取得する

    SaleForceの開発者ガイドで公開したデータの問合せ方法の1つ:REST API/Query

    PHPでのデータ問合せ方法(cUrl利用):

    $query = "SELECT フィールド名 FROM テーブル名";
    $query = http_build_query(['q' => $query]);
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $access_token, //前の手順で取得したアクセストークン(文字列)
    ]);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_URL, 'https://SaleForce環境名.salesforce.com/services/data/v20.0/query/?' . $query);
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 7);
    curl_setopt($ch, CURLOPT_TIMEOUT, 7);
    //お問い合わせ
    $response = curl_exec($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $json = json_decode($response, true);
    curl_close($ch);
    

     まとめ 

    今回の記事では、SaleForceからデータを取得できるまでの手順をご紹介いたしました。
    learningBOXもSaleForceと連携可能ですので、ぜひお試しください。