OAuth2 Client Credentialsフローを使ったシンプルなSalesforceとのAPI連携

Winter '23 リリースから、Salesforce でも [OAuth2 Client Credentials フロー (クライアントログイン情報フロー) がサポート](https://help.salesforce.com/s/articleView?id=release-notes.rn_security_client_credentials_flow.htm&release=240&type=5)されるようになりました。このフローは、直接ユーザ認証を行わず、クライアントの認証のみでアクセストークンを発行します。Salesforce の言葉で言い換えると、接続アプリケーションのコンシューマ鍵とコンシューマの秘密だけでアクセストークンを発行するフローになります。クライアント側で Salesforce のユーザ Id やパスワードを保持する必要はありません。その代わりに、接続アプリケーションには固定のユーザを紐づける必要があります。このフローはいわゆる API ユーザを利用した連携、すなわち、ユーザのコンテキストを必要としない、かつブラウザを介さないサーバ間連携によくフィットします。この記事では、Salesforce における Client Credentials フローの設定方法と注意事項を紹介します。

なお、この Client Credentials フローの詳細は [RFC 6749 Section 4.4](https://www.rfc-editor.org/rfc/rfc6749#section-4.4) で定義されています。

## 設定方法
アプリケーションマネージャから新規接続アプリケーションを作成します。設定画面内の 
`クライアントログイン情報フローを有効化` のチェックを ON にします。

![Salesforce Client Credentials Flow Setting Checkbox](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/e0fc7401-46dd-f012-be3a-e42215b717e4.png)

`コールバック URL` は設定上必須項目ですが任意のURLで構いません。

保存後、[Manage] ボタン > [ポリシーを編集] ボタンから、[クライアントログイン情報フロー] セクションの `別のユーザとして実行` 欄から実行ユーザを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/f7e51bf0-653d-c766-2897-2a6927153d81.png)

:::note info
同じく Spring'23 で、[API Only のライセンスが無料でいくつか付与](https://help.salesforce.com/s/articleView?id=release-notes.rn_api_integration_license.htm&release=242&type=5)されるようになりました:tada:
みなさんの組織内には「Salesforce Integration」ライセンスの
「Salesforce API Only System Integrations」 という標準プロファイルが作成されているはずです。Salesforce ライセンスのプロファイルを利用して API 限定ユーザを設定してももちろん良いですが、UI からログインしない場合はこの無料で利用できる新しいライセンスを活用するとお金がかからずおすすめです。なお、Salesforce API Only System Integrations プロファイルやこれをコピーしたプロファイルではオブジェクト権限やFLSを設定できないため、「Salesforce API Integration 」の**権限セットライセンス**を使用して権限を付与するようにしてください。
:::

## アクセストークンをリクエストする
リクエストには以下を含めます。
* `client_id`: 接続アプリケーションのコンシューマ鍵
* `client_secret`: 接続アプリケーションのコンシューマの秘密
* `grant_type`: client_credentials を固定で指定します

curl コマンドでアクセストークンをリクエストする例は以下の通りです。

```
$ curl -X POST 'https://MyDomain.my.salesforce.com/services/oauth2/token?grant_type=client_credentials&client_id=********&client_secret=********'
{"access_token":"*********","signature":"*********","scope":"api","instance_url":"https://MyDomain.my.salesforce.com","id":"https://login.salesforce.com/id/00D28000001it8SEAQ/0050I00000BBOhGQAX","token_type":"Bearer","issued_at":"1681350812951"}
```

### 考慮事項
* ホスト名に login.salesforce.com は利用できず、私のドメインを指定する必要があるため注意してください。
  * login.salesforce.com に対してリクエストすると、`{"error":"invalid_grant","error_description":"request not supported on this domain"}` が返されます。
* アクセストークンは、先ほど接続アプリケーションで選択したユーザとして発行されます。
* リフレッシュトークンは発行されません。

## おわりに
Client Credentials フローを用いるとシンプルに Salesforce との API 連携が実現できます。クライアント側に Salesforce のユーザ名やパスワードを直接保持する必要もありません。ユーザコンテキストを必要としないサーバ間連携では良い選択肢になると思います。

また、簡単だからという理由で API 連携に[ユーザ名パスワードフロー](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_username_password_flow.htm&language=ja&type=5)を使用しつづけていませんか。リンク先の Help 記事にも記載がありますがこのフローは非推奨ですし、元になっている Resource Owner Password Credentials Grant は次の OAuth 2.1 では仕様から除去される予定です。[設定上、ユーザ名パスワードフローをブロックすることも可能](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_disable_username_password_flow.htm&type=5)になりましたし、この Client Credentials フロー含め、この機会に適切な OAuth フローに見直すと良いのではないでしょうか。

## 参考情報
* [Help - OAuth 2.0 Client Credentials Flow for Server-to-Server Integration](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_client_credentials_flow.htm&type=5)