VISASQ Dev Blog

ビザスク開発ブログ

SalesforceとDocuSignをJWT認証+指定ログイン情報で安全に連携する

はじめに

こんにちは!Salesforceチームの原田です。
今回は、SalesforceとDocuSignをJWT認証+指定ログイン情報で安全に連携する方法をご紹介します。
これを使えば、DocuSign APIを安全かつシンプルに呼び出すことができ、環境ごとの設定切り替えやトークン管理も自動化できます。

従来のJWT認証実装の課題

Apexの HttpRequest() を使えばDocuSign APIにアクセスできますが、JWT認証を組み込むとコードがかなり長くなります。

例:従来のJWT認証コード(抜粋)

// 1. JWTトークンを作成
String jwtToken = createJWTToken();

// 2. アクセストークンを取得
HttpRequest tokenReq = new HttpRequest();
tokenReq.setEndpoint('https://account-d.docusign.com/oauth/token');
tokenReq.setMethod('POST');
tokenReq.setHeader('Content-Type', 'application/x-www-form-urlencoded');
tokenReq.setBody(
    'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' +
    '&assertion=' + jwtToken
);
HttpResponse tokenRes = new Http().send(tokenReq);

// レスポンス解析(アクセストークン抽出)
String accessToken = (String) JSON.deserializeUntyped(tokenRes.getBody()).get('access_token');

// 3. DocuSign API呼び出し
HttpRequest apiReq = new HttpRequest();
apiReq.setEndpoint('https://demo.docusign.net/restapi/v2.1/accounts/' + accountId + '/envelopes/' + envelopeId);
apiReq.setMethod('PUT');
apiReq.setHeader('Authorization', 'Bearer ' + accessToken);
apiReq.setHeader('Content-Type', 'application/json');
apiReq.setBody(JSON.serialize(new Map<String, String>{
    'status' => 'voided',
    'voidedReason' => 'void by Salesforce'
}));
HttpResponse apiRes = new Http().send(apiReq);

注: 実際にはこの前後に JWT生成処理・証明書署名・Base64エンコード・例外処理 などが入り、数十行のコードになります。
また、環境(Sandbox/本番)の切り替えやトークン期限変更のたびに修正が必要で、保守コストも高くなります。

改善策:指定ログイン情報を使う

Salesforce指定ログイン情報は、外部システムとの認証・接続設定を一元管理できる機能です。
DocuSignとJWT認証で連携する場合も、この機能を使うことでApexコード側から認証処理をほぼ排除できます。

Apexで直接認証処理を実装する場合との比較

  • JWT生成、トークン取得、Authorizationヘッダー付与、期限切れ時の再取得をSalesforceが自動処理
  • 秘密鍵やクライアントIDは暗号化保管、環境切り替えや鍵更新は管理画面で完結
  • コードはAPI呼び出しに専念でき、実装量と保守コストが大幅減

指定ログイン情報を使った簡易サンプル

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:DocuSign/...'); // 指定ログイン情報の名前を指定
req.setMethod('GET'); // 例:エンベロープ一覧取得など用途に応じて変更
HttpResponse res = new Http().send(req);

設定手順

1. Salesforceで証明書を作成

  1. [証明書と鍵の管理] から新規証明書を作成(自己署名証明書でOK)

  2. 作成した証明書をダウンロード

  3. 公開鍵を抽出
    openssl x509 -in DocuSign_Integration_Cert.crt -pubkey -noout > public_key.pem

2. DocuSignでアプリケーションを作成

  1. DocuSign管理画面の[アプリとキー]からアプリケーションを作成
  2. 手順1で作成した公開鍵(public_key.pemをアップロード
  3. リダイレクトURIを追加
  4. 下記のURLにアクセスし、アプリの有効化
    https://account-d.docusign.com/oauth/auth? response_type=code &scope=signature%20impersonation &client_id= [インテグレーションキー] &redirect_uri= [リダイレクトURI]

3. Salesforceで接続設定を作成

  1. [外部ログイン情報] を作成(詳しい項目は下記画像参照)
    • IDプロバイダーURL: https://account-d.docusign.com/oauth/token (本番環境では https://account.docusign.com/oauth/token
    • Issuer(iss): [インテグレーションキー]
    • Subject(sub): [ユーザーID]
    • Audience(aud): account-d.docusign.com(本番環境ではaccount.docusign.com
    • scope: signature impersonation

  1. [指定ログイン情報] を作成(詳しい項目は下記画像参照)
    • URL https://demo.docusign.net/restapi/v2.1 (本番環境では https://www.docusign.net/restapi/v2.1
    • 外部ログイン情報と紐付け
  2. 権限セットを作成し、指定ログイン情報の使用権限を付与

  3. 対象ユーザーに権限セットを割り当て

接続確認(開発者コンソール)

設定が完了したら、Salesforceの開発者コンソールで以下のコードを実行し、DocuSign APIと疎通できるか確認します。

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:DocuSign/login_information');
req.setMethod('GET');

HttpResponse res = new Http().send(req);
System.debug('Status=' + res.getStatusCode());
System.debug(res.getBody()); // 200が返り、ユーザー情報が含まれていればOK

実装例:エンベロープ無効化

指定ログイン情報を利用したDocuSignエンベロープ無効化の実装例です。

private static HttpResponse sendVoidRequest(String accountId, String envelopeId, String reason) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint('callout:DocuSign/accounts/' + accountId + '/envelopes/' + envelopeId);
    req.setMethod('PUT');
    req.setHeader('Content-Type', 'application/json');
    req.setBody(JSON.serialize(new Map<String, String>{
        'status' => 'voided',
        'voidedReason' => reason
    }));
    return new Http().send(req);
}

おわりに

指定ログイン情報とJWT認証を組み合わせることで、DocuSign連携をシンプルかつ安全に実装でき、運用負荷も大幅に削減できるようになります。

同じような課題を抱えている方にとって、本記事が少しでも参考になれば幸いです!

💬 ビザスクではエンジニアの仲間を募集しています!
少しでもビザスク開発組織にご興味を持たれた方は、ぜひ一度カジュアルにお話ししましょう!
developer-recruit.visasq.works