LoginSignup
3
2

Salesforceのサーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローを試してみる

Last updated at Posted at 2023-07-02

1. やってみたこと

  • Salesforceのサーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローで認証を行う
  • 適当なSalesforce APIにアクセスしてデータ連携を行う

2. バージョンや背景など

  • Summer '23 Patch 10.7 (2023/7/2時点)
  • sfdx-cli/7.206.6 darwin-x64 node-v18.15.0
% sfdx version
sfdx-cli/7.206.6 darwin-x64 node-v18.15.0
  • スクラッチ組織で試す
  • JWTの作成にJava, Mavenを使用します
  • Java
% java --version
openjdk 17.0.7 2023-04-18
OpenJDK Runtime Environment Temurin-17.0.7+7 (build 17.0.7+7)
OpenJDK 64-Bit Server VM Temurin-17.0.7+7 (build 17.0.7+7, mixed mode, sharing)
  • Maven
% mvn --version
Apache Maven 3.9.3 (21122926829f1ead511c958d89bd2f672198ae9f)

3. 試したこと

API インテグレーション用の OAuth 設定の有効化
サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフロー
安全な Salesforce API ユーザの作成
こちらを参照しながら試していきます

3.1. スクラッチ組織を作成する

以下のようなコマンドでスクラッチ組織を作成します
デフォルトのDevHub組織が既に作成および認証済みとします

sfdx org create scratch --edition developer --alias demo

Your scratch org is ready.というメッセージが表示され、sfdx org listで作成したスクラッチ組織を確認できます

以下のコマンドで作成したスクラッチ組織をブラウザで表示します

sfdx org open -o demo

3.2. サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローの接続アプリケーションを設定する

サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローの接続アプリケーションを作成します
API インテグレーション用の OAuth 設定の有効化
こちらを参照しながら作成します

3.2.1. 署名証明書を作成する

JWTベアラーフローのための秘密鍵、署名証明書を作成します
ここでは自己署名証明書を作成します

秘密鍵の作成
openssl genrsa 2048 > demo.pem
署名リクエストの作成
openssl req -new -key demo.pem -out demo.csr
署名証明書の作成
openssl x509 -req -days 365 -in demo.csr -signkey demo.pem -out demo.crt

署名証明書は、接続アプリケーションの設定に使用します

3.2.2. 新規接続アプリケーションの作成

  • 設定 > アプリケーション > アプリケーションマネージャ新規接続アプリケーションをクリックします

3.2.3. 基本情報の入力

基本情報を入力します

  • 接続アプリケーション名, API 参照名, 取引先責任者 メールを設定します

image.png

3.2.4. OAuth設定の入力

OAuth設定を入力します

  • OAuth 設定の有効化にチェックします
  • コールバック URLを入力します
    • JWTベアラーフローでは使用しない設定のため、任意のもので大丈夫です。ここではhttps://localhost:19999としました
  • デジタル署名を使用をチェックします
    • ファイルを選択をクリックして、3.2.1. 署名証明書を作成するで作成した自己証明書を設定します
    • ここではdemo.crtになります
  • 選択した OAuth 範囲を選択します
    • 今回はSalesforce APIを利用するため、API を使用してユーザデータを管理 (api)を選択します
    • いつでも要求を実行 (refresh_token, offline_access)を選択します
  • Web サーバフローの秘密が必要をチェックします
  • 更新トークンフローの秘密が必要をチェックします
  • すべてのトークンを調査をチェックします

image.png

3.2.5. 設定の保存

  • 保存します

3.3. 接続ユーザーの作成

接続ユーザーはAPI限定ユーザーで作成します

安全な Salesforce API ユーザの作成

組織でインテグレーションの目的にのみ使用する特別なユーザを作成します。こうすることで、実際のユーザが組織を離れた場合でも、いつでも適切な権限を持つユーザが存在することになります。

3.3.1. API限定ユーザーの作成 (1)

「Salesforce Integration」ライセンスの「Salesforce API Only System Integrations」 というプロファイルでAPI限定ユーザーを作成することができます
このライセンスを利用するとSalesforceライセンスを消費することがありません

  • 設定 > ユーザ > ユーザ新規ユーザをクリックします
  • ユーザライセンスにSalesforce Integrationを設定します
  • プロファイルにSalesforce API Only System Integrationsが自動的に設定されます
  • 保存して、ユーザを作成します

image.png

3.3.2. API限定ユーザーの作成 (2)

以下の方法でもAPI限定ユーザーを作成することができます
この場合、Salesforceライセンスを消費しますので、注意が必要です

3.3.2.1. API限定ユーザープロファイルの作成

  • 設定 > ユーザ > プロファイル新規プロファイルをクリックします
  • 既存のプロファイルを選択します
    • 新しく作成するユーザープロファイルの基となるものを選択します
    • ここでは標準ユーザを選択します
  • プロファイル名を入力します
    • ここではapionlyとします

image.png

  • 編集をクリックします
  • システム管理者権限 > API 限定ユーザをチェックします
  • 保存します

image.png

3.3.2.2. ユーザーの作成

  • 設定 > ユーザ > ユーザ新規ユーザをクリックします
  • ユーザライセンスにSalesforceを設定します
  • プロファイルに3.3.2.1. API限定ユーザープロファイルの作成で作成したプロファイルを設定します
  • 保存して、ユーザを作成します

image.png

3.4. 接続アプリケーションのポリシーを設定

接続アプリケーションで認証できるプロファイルを設定します

  • 設定 > アプリケーション > アプリケーションマネージャの該当アプリケーションの右端▼からManageをクリックします

  • ポリシーを編集をクリックします

  • OAuth ポリシー > 許可されているユーザ管理者が承認したユーザは事前承認済みに設定します

    • このオプションを有効にすると、現在このアプリケーションを使用しているすべてのユーザがアクセスを拒否されます。アプリケーションを使用中のユーザを確認するには、接続アプリケーションの OAuth 利用状況レポートを参照してください。というダイアログが表示されるので、OKをクリックします
  • 保存します

  • プロファイル > プロファイルを管理するで、3.3. 接続ユーザーの作成で設定したプロファイルを設定します

image.png

3.5. アクセストークンを要求する

サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフロー
こちらを参照しながら試していきます

3.5.1. client_idの確認

  • 設定 > アプリケーション > アプリケーションマネージャの該当アプリケーションの右端▼から参照をクリックします
  • API (OAuth 設定の有効化) > コンシューマキーと秘密のコンシューマの詳細を管理をクリックします
  • 確認コードを要求されたら入力して、検証をクリックします
  • コンシューマ鍵コンシューマの秘密が表示されます
  • コンシューマ鍵client_idコンシューマの秘密client_secretに該当します
  • コンシューマ鍵のみをコピーして控えます

3.5.2. JWTの作成

  • JWT ヘッダーを次の形式で作成します{"alg":"RS256"}
  • JWT の JSON 要求セットを作成します
    • iss: client_id (コンシューマ鍵)
    • sub: 接続するユーザー名
    • aud: 認可サーバーの URL
      • https://login.salesforce.com または https://test.salesforce.com
      • 本番環境では前者、テスト環境(SandboxやScratch)では後者になります
    • exp: 有効性は 3 分以内のアサーションの有効時間である必要があります (UTC で測定された 1970-01-01T0:0:0Z からの秒数として表記)

JSON 要求セットは以下のような形式になります

{"iss": "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQ
ukXnVjzRgSuQqGn75NL7yfkQcyy7", 
"sub": "my@email.com", 
"aud": "https://login.salesforce.com", 
"exp": "1333685628"}

今回は、JavaでJWTを作成します

3.5.2.1. 秘密鍵をDERフォーマットに変換

Javaで秘密鍵を読み取るために、3.2.1. 署名証明書を作成するで作成した秘密鍵をDERフォーマットに変換します

秘密鍵をDERフォーマットに変換
openssl pkcs8 -topk8 -inform PEM -outform DER -in demo.pem -out demo.der -nocrypt

3.5.2.2. JavaでJWTを作成する

今回、JWTを作成するライブラリとして以下を使用することとします
auth0/java-jwt

また、Javaのjshellスニペットで処理を記述したいと思います
johnpoth/jshell-maven-pluginを使用して、ライブラリの依存性解決を行います

以下に続くファイルを作成します

JWTを作成するために必要な情報を環境変数に設定します

env
export JWT_ISS="******"
export JWT_SUB="******@******.***"
export JWT_AUD="https://test.salesforce.com"
export JWT_PRIVATEKEY_PATH=demo.der

jshell-maven-plugin、およびライブラリjava-jwtを使用するために以下のファイルを作成します

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>1.0</version>
 
  <dependencies>
    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>4.4.0</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>com.github.johnpoth</groupId>
          <artifactId>jshell-maven-plugin</artifactId>
          <version>1.3</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

</project>

jshellのスニペットファイルを作成します

demo.jshell-snipet
import java.time.OffsetDateTime

// Definitions
var jwtHeader = """
{"alg":"RS256"} """

var iss = System.getenv("JWT_ISS")
var sub = System.getenv("JWT_SUB")
var aud = System.getenv("JWT_AUD")
var privateKeyPath = System.getenv("JWT_PRIVATEKEY_PATH")
var exp = OffsetDateTime.now().plusMinutes(3).toInstant()

// generate RSAKey from private_key file
import java.nio.file.Paths
import java.nio.file.Files
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.security.interfaces.RSAKey

var privateKeyFile = Paths.get(privateKeyPath)
var privateKeyContent = Files.readAllBytes(privateKeyFile)
var keySpec = new PKCS8EncodedKeySpec(privateKeyContent)
var keyFactory = KeyFactory.getInstance("RSA")
var rsaPrivateKey = keyFactory.generatePrivate(keySpec)

// Generate JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.JWT

var algo = Algorithm.RSA256((RSAKey)rsaPrivateKey)

var jwtBuilder = JWT.create()
jwtBuilder.withIssuer(iss)
jwtBuilder.withSubject(sub)
jwtBuilder.withAudience(aud)
jwtBuilder.withExpiresAt(exp)
jwtBuilder.withHeader(jwtHeader)

var jwt = jwtBuilder.sign(algo)

// Print generated JWT
System.out.println(jwt)

/exit

上記の準備ができたら、以下のコマンドを実行します

JWTの作成に必要な情報を環境変数に設定
source env
JWTの作成
mvn jshell:run -Djshell.scripts=demo.jshell-snipet

コマンドが正常に実行されたら、JWTが出力されます

3.5.3. アクセストークンの取得

以下のようなコマンドで、アクセストークンを取得することができます

assertionには3.5.2. JWTの作成で作成したJWTを設定します

アクセストークンの取得
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' 'https://MyDomainName.my.salesforce.com/services/oauth2/token' --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' --data-urlencode 'assertion=*********'

上記のhttps://MyDomainName.my.salesforce.comの部分はhttps://test.salesforce.comでも代替可能です

そうすると以下のようなレスポンスが返ってきます
access_tokenの値を使用してAPIにアクセスを行うことができます
また、instance_urlの値はユーザーの組織のインスタンスを示すURLになります

response
{
  "access_token": "******",
  "scope": "api",
  "instance_url": "https://MyDomainName.my.salesforce.com",
  "id": "https://test.salesforce.com/id/******/******",
  "token_type": "Bearer"
}

JWTベアラーフローでは、リフレッシュ トークンは発行されません
セッションタイムアウト内で取得されるアクセストークンは同じものになります

4. Appendix

4.1. OAuth 範囲refresh_tokenを設定しなかった場合は?

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきます
refresh_tokenが必要だというエラーメッセージがあり、refresh_tokenの設定は必要なことが分かります

response
{
  "error": "invalid_request",
  "error_description": "refresh_token scope is required and the connected app should be installed and preauthorized."
}

4.2. JWTのaudにhttps://MyDomainName.my.salesforce.comを設定したら?

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきます
audが正しくないことが分かります

response
{
  "error": "invalid_grant",
  "error_description": "audience is invalid"
}

4.3. JWTのexpに設定したアサーションの有効時間を超えてアクセストークンを取得しようとしたら?

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきます
有効期限切れであることが分かります

response
{
  "error": "invalid_grant",
  "error_description": "expired authorization code"
}

4.4. 接続アプリケーションに設定したデジタル証明書の有効期限を超えたら?

TODO

4.5. API限定ユーザーでWebログインしようとしてみたら?

以下のように表示され、Webログインが拒否されたことが分かります

image.png

4.6. 正しくない秘密鍵でJWTを作成してアクセストークンを取得しようとしたら?

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきます
client credentialsが正しくないことが分かります

response
{
  "error": "invalid_client",
  "error_description": "invalid client credentials"
}

参考

API インテグレーション用の OAuth 設定の有効化
サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフロー
安全な Salesforce API ユーザの作成

おわり。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2