3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

1. やってみたこと

Salesforceのサーバ間インテグレーション用の OAuth 2.0 クライアントログイン情報フローでprivate_key_jwt方式を利用できるか確認する

1.1. 背景など

OAuth 2.0 クライアント認証
こちらの記事を参考にすると、OAuth 2.0 クライアント認証には以下の方式があるようです

  • client_secret_post
    • 認可サーバが生成したclient_idclient_secretをクライアントに提供する
    • クライアントはトークンリクエストする際にclient_idclient_secretを含めて送信する
  • client_secret_basic
    • 認可サーバが生成したclient_idclient_secretをクライアントに提供する
    • クライアントがトークンリクエストする際にBasic認証を使用して送信する
    • client_id:client_secretをbase64エンコードしたものをAuthorizationヘッダーに埋め込む
  • client_secret_jwt
    • 認可サーバが生成したclient_idclient_secretをクライアントに提供する
    • クライアントがトークンリクエストする際にJWTを作成して、それを含めて送信する
    • JWTを生成する際にclient_secretを使用して署名する
  • private_key_jwt
    • 認可サーバが生成したclient_idをクライアントに提供する
    • クライアントで秘密鍵と署名証明書を作成します
    • クライアントで作成した署名証明書を認可サーバに提供する
    • クライアントがトークンリクエストする際にJWTを作成して、それを含めて送信する
    • JWTを生成する際に秘密鍵を使用して署名する

サーバ間インテグレーション用の OAuth 2.0 クライアントログイン情報フロー
こちらのSalesforceマニュアルでは、client_secret_postclient_secret_basicの方法について記載があります

ただし、client_idclient_secretへのアクセス権を持つすべてのユーザまたはアプリケーションはアクセストークンを取得することができます
Salesforceマニュアルでは、そのようなセキュリティリスクを考慮してコンシューマの秘密(client_secret)を定期的に変更することを推奨しています

コンシューマの秘密の変更を行うと、そのタイミングでクライアントはアクセストークンを取得できない期間がどうしても発生してしまいます
それであればそもそもclient_secretを使用しない方法の方が幾分かリスクは減り、運用コストも軽減するのではないかと思います

Salesforce側の接続アプリケーションのclient_idと、クライアント側の秘密鍵に同時にアクセスすることは困難であると思いますので、JWTベアラーフローを検討したいところです

上記を踏まえて、クライアントログイン情報フローでprivate_key_jwt方式が使用できるのかを見てみたいと思いました

2. バージョンなど

  • Summer '23 Patch 10.8 (2023/7/3時点)
  • 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. 試したこと

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

3.1. 確認する内容

OAuth 2.0 クライアント認証
こちらを参照すると、JWT要求セットの要素は以下のように記載されていました

  • JWT要求セットの要素
    • iss: client_id
    • sub: client_id
    • aud: token endpoint

サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフロー
SalesforceのJWTベアラーフローのマニュアルでは、JWT要求セットの要素は以下のように設定すると記載されていました

  • JWT要求セットの要素
    • iss: client_id (コンシューマ鍵)
    • sub: 接続するユーザー名
    • aud: 認証サーバの URL
      • https://login.salesforce.com または https://test.salesforce.com
      • 本番環境では前者、テスト環境(SandboxやScratch)では後者になります

subに設定する値がキーに思います
subに設定する値について以下のケースを試してみます

Case 1) subclient_id である場合

以下の組み合わせを試します

  • sub
    • client_id
  • aud
    • https://test.salesforce.com

推測としては、subclient_idだとどこに接続して良いのか分からない感じがします

Case 2) sub が クライアントログイン情報フローで別のユーザとして実行に設定されているユーザのユーザー名である場合

SalesforceのJWTベアラーフローでは、subに接続ユーザのユーザー名を設定するので、このケースを試してみます
以下の組み合わせを試します

  • sub
    • クライアントログイン情報フローで別のユーザとして実行に設定されているユーザのユーザー名
  • aud
    • https://test.salesforce.com

Case 3) sub が クライアントログイン情報フローで別のユーザとして実行に設定されているユーザとは異なるユーザのユーザー名である場合

他のユーザー名を指定した場合はどうなるかを試してみます
以下の組み合わせを試します

  • sub
    • クライアントログイン情報フローで別のユーザとして実行に設定されているユーザとは異なるユーザのユーザー名
  • aud
    • https://test.salesforce.com

3.2. クライアントログイン情報フロー用の接続アプリケーションの作成とアクセストークン取得の確認

まずは、クライアントログイン情報フローでアクセストークンが取得できる環境を準備します

Salesforceのサーバ間インテグレーション用の OAuth 2.0 クライアントログイン情報フローを試してみる
こちらを参照して環境を準備して正常にアクセストークンを取得できることを確認します

3.3. JWTベアラーフローのための準備

Salesforceのサーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローを試してみる
こちらを参照してJWTベアラーフローのための準備を行います

クライアントログイン情報フローでは、接続アプリケーションのOAuth ポリシー > 許可されているユーザすべてのユーザは自己承認可能で動作していたのでこの設定はこのままにしておきます
接続アプリケーションのプロファイルもここでは設定しないままにしておきます

JWTの作成やアクセストークンの取得方法についても上記記事の内容を利用します

3.4. subclient_id である場合

JWTのJSON要求セットは以下のような形式です

JWTのJSON要求セット
{
  "iss": client_id, 
  "sub": client_id, 
  "aud": "https://test.salesforce.com", 
  "exp": "1333685628"
}

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきました
承認されていないという旨のメッセージです

response
{
  "error": "invalid_grant",
  "error_description": "user hasn't approved this consumer"
}

user hasn't approved this consumerで検索すると以下のページがヒットしました

{ "error" : "invalid_grant", "error_description" : "user hasn't approved this consumer"}
Salesforce JWT User Hasn't Approved This Consumer (Again)

また、こちらの記事の内容も必要そうです
エラー「失敗: アクセスが承認されていません」

該当する接続アプリケーションの詳細 ([設定] | [アプリケーションを管理する] | [接続アプリケーション]) に移動すると、[OAuth ポリシー] セクションで [許可されているユーザー] が [管理者が承認したユーザーは事前承認済み] に設定されている可能性があります。
1.特定のユーザーグループにこのアプリへのアクセスを許可する手順は次のとおりです。
[プロファイルを管理する] とプロファイル関連リストを活用して、該当する Salesforce プロファイル (および関連するユーザー) へのアクセスを承認します。

接続アプリケーションに以下の設定を行います

  • OAuth ポリシー > 許可されているユーザ管理者が承認したユーザは事前承認済みに設定
  • プロファイルクライアントログイン情報 > 別のユーザとして実行に設定されているユーザーのプロファイル

もう1度アクセストークンを取得を試してみます

以下のようなレスポンスが返ってきました

response
{
  "error": "invalid_grant",
  "error_description": "user hasn't approved this consumer"
}

う〜ん、だめそうです。。。

3.5. Case 2) sub が クライアントログイン情報フローで別のユーザとして実行に設定されているユーザのユーザー名である場合

JWTのJSON要求セットは以下のような形式です

JWTのJSON要求セット
{
  "iss": client_id, 
  "sub": クライアントログイン情報フローで別のユーザとして実行に設定されているユーザのユーザー名, 
  "aud": "https://test.salesforce.com", 
  "exp": "1333685628"
}

3.4.で設定したOAuth ポリシー > 許可されているユーザプロファイルは設定したままにします

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきました

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

正常にアクセストークンを取得することができました

3.6. Case 3) sub が クライアントログイン情報フローで別のユーザとして実行に設定されているユーザとは異なるユーザのユーザー名である場合

続いて、他のユーザーでアクセストークンを取得しようとするとどうなるのか見てみます

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきました

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

正常にアクセストークンを取得することができました

普通にJWTベアラーフローが動作したような感じです

3.7. Case 4) クライアントログイン情報フローでアクセストークンを取得してみる

接続アプリケーションには、クライアントログイン情報フローとJWTベアラーフローの両方の設定がある状態です
この状態でクライアントログイン情報フローでアクセストークンを取得しようとするとどうなるか見てみます

アクセストークンを取得しようとすると以下のようなレスポンスが返ってきました

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

正常にアクセストークンを取得することができました

3.8. 接続ユーザーのログイン履歴を見てみると

  • 設定 > ユーザ > ユーザで該当ユーザを選択します
  • ログイン履歴をクリックします

ここを見てみると、ログインサブ種別OAuth クライアントログイン情報となっているものとそうでないものがあります

image.png

つまり、クラアインログイン情報フローでリクエストするとログインサブ種別OAuth クライアントログイン情報になり、JWTベアラーフローであればログインサブ種別は空欄になるようです

クライアントログイン情報フローとJWTベアラーフローの併用ができることが分かりました

4. まとめると...

  • クライアントログイン情報フロー
    • client_secret_postclient_secret_basicの方式を利用できる
    • client_secret_jwtの方式は未検証のため不明
    • private_key_jwtの方式は利用不可
  • 接続アプリケーション
    • クライアントログイン情報フローとJWTベアラーフローの併用した設定ができる
    • クライアントログイン情報フローでリクエストすると、クライアントログイン情報 > 別のユーザとして実行に設定したユーザーで実行される
    • JWTベアラーフローでは、他のユーザーも利用することができる

参考

OAuth 2.0 クライアント認証
サーバ間インテグレーション用の OAuth 2.0 クライアントログイン情報フロー
サーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフロー
Salesforceのサーバ間インテグレーション用の OAuth 2.0 クライアントログイン情報フローを試してみる
Salesforceのサーバ間インテグレーション用の OAuth 2.0 JWT ベアラーフローを試してみる
{ "error" : "invalid_grant", "error_description" : "user hasn't approved this consumer"}
Salesforce JWT User Hasn't Approved This Consumer (Again)
エラー「失敗: アクセスが承認されていません」

おわり。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?