Salesforce で外部サービスを使うときには「指定ログイン情報」を使いましょう

これは多分 [Salesforce Platform Advent Calendar 2019](https://qiita.com/advent-calendar/2019/salesforce-platform) の 23日目。

昨日は、[えんかな](https://qiita.com/endou_kanako)さんの「[新機能であそぼ](https://endoukanako.hatenablog.com/entry/2019/12/22/083000)」で、明日はクリスマスイブ!!!!!!!!11 な弊社[エヴァンジェリスト](https://qiita.com/hrk623)のなんかです。

そして、今日はかしゆかの誕生日です、おめでとうおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおうわああああああああああああああああああああああああああああああああああ (あとで社内に公開する可能性のあるネタだと忘れての書き込みです。知らんぷりしましょう)。

# いいからつべこべ言わずに「指定ログイン情報」を使え

みなさんは、Salesforce Platform から、外部システムへアクセスする場合にどのような方法を使いますか？

そうですね、`Apex`で [HTTP コールアウト](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_callouts_http.htm) することが多いでしょう。たしかにね、実にカンタンです。`HttpRequest()` に URLやメソッドを指定すれば、あら不思議、こんなカンタンに外部Webサービスからデータ取れてよかったのかしらん。みたいになりますね。

```java
public class HttpCalloutSample {

  // Pass in the endpoint to be used using the string url
  public String getCalloutResponseContents(String url) {

    // Instantiate a new http object
    Http h = new Http();

     // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
    HttpRequest req = new HttpRequest();
    req.setEndpoint(url);
    req.setMethod('GET');

    // Send the request, and return a response
    HttpResponse res = h.send(req);
    return res.getBody();
  }
}
```
※ `Apex`なのに`Java`と宣言する悲しさよ。

はい、ダイレクトにURLを指定してApexコールアウトする時代は終わりました。やめましょう。

## なぜ、HttpRequest() をやめるべきなのか

いや、`HttpRequest()`自体は良いのです。URL をダイレクトに指定する方法をやめましょうということです。なぜなら、相手先の認証の仕組みなどが変わったときにコードを変更しなければならなくなるからです。

「認証...?(´ε｀；)ｳｰﾝ…」

今どきのまともな精神を保ったWebサービスであれば、認証・認可の仕組みがついていることが基本でしょう。Google API も Twitter も、多くのサービスが認証を経由して APIを利用するケースとなっています。このときに、認証の仕組みを含めて`Apex`で実装することはおすすめしません。一番の理由は「実装が面倒」だからです。面倒 = アンセキュアです。面倒なことを正しく実装しようとすると、コードが溢れんばかりになります。だから、ついつい手を抜きがちです。手を抜く = アンセキュアです。だいたい、満身創痍のときに書いたコードはアンセキュアになります。

だから、認証部分をまるっと対応してくれる「指定ログイン情報」+「認証プロバイダ」を活用しましょう

これらを利用することにより、そもそも`Apex`でのプログラミングすら不要になる可能性があります。

「プログラミングがいらない...?🤔」

そうなのです。いつの間にやら使えるようになっていた「[外部サービス](https://help.salesforce.com/articleView?id=external_services.htm&type=5)」を利用すると、ノーコードで、フローやプロセスビルダーから、外部のWebサービスを利用することができるようになります。おや、いつの間にそんなことが。

Salesforce Platform の標準機能を組み合わせることで、「安全に」「外部サービスと」「連携」できます。すごいやん。

もし、コードを書くということになっても、認証にまつわる部分は一切合切コーディング不要です。例えば`My_Named_Credential` という指定ログイン情報を作り、認証にまつわる様々なものは、そこで定義しておけば、コードは次のようにまとめられます。

```java
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:My_Named_Credential/some_path');
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());
```

`HttpRequest()` の `setEndpoint` する際に `callout:` + `指定ログイン情報の名前`を組み合わせるだけで、登録されたURLに対して、設定された「認証プロバイダ」を使って、勝手に認証してくれます。

「OAuthでリフレッシュトークン取らなきゃ...」「返却されたメアドで、Salesforceアカウントも認証させなきゃ...」細かいことを考える必要はありません。「認証プロバイダ」と、「指定ログイン情報」が、全てよしなにやってくれます。もう、むしろなぜ、これを使わない。

# 設定の手順

Salesforce のことだ、どうせ設定が複雑怪奇だったりするんだろう...? と思った貴方。Salesforce でカンタンに実現できないことを、複雑怪奇な設定で回避しようとした経験がありますね? いけません。そういったときは、`Apex`に頼ることも一考です。ご考慮ください。そう、そのフロー。デバッグできないレベルじゃないですか？繰り返しが多すぎませんか？ガバナの制約に不安を感じませんか？そう、すべてを設定に投げればよいわけではないのです、ある程度は`Apex`などに頼ることも必要です。すべてを設定に投げてはいけません。

閑話休題。

もし、「指定ログイン情報」を使って外部のサービスとの認証をよしなにやろうとした場合、どのようにすればよいでしょうか...？大きな手順は次のとおりです。

1. 「認証プロバイダ」を設定する
2. 「指定ログイン情報」を設定する

なお、認証が不要だったり、ユーザーパスワード認証、JWTなトークンな場合では 1.すら不要です。カンタンすぎませんか？そうなんです、特定の場合を除き、原則楽です。

## 「認証プロバイダ」を設定する

「設定」の「認証プロバイダ」で、おもむろに「新規」ボタンです。

このように、Salesforce ではいくつかの、よく利用されるであろう認証プロバイダが登録されています。

<img width="476" alt="Screen Shot 2019-12-23 at 10.29.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/1d38d3ad-c787-d71b-40b3-40585c7b88ad.png">

- Facebook
- Janrain
- Salesforce
- Open ID Connect
- Microsoft アクセスコントロールサービス
- Linkedin
- Twitter
- Google
- GitHub

※ 2019/12/23 現在

これらに関しては、完全に設定だけで、認証プロバイダを準備できます。プロバイダごとに設定項目は変わりますし、プロバイダ側の設定も必要になります。例えば Twitter だと次の項目を埋めることになります。

<img width="783" alt="Screen Shot 2019-12-23 at 10.32.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/e3a2afe6-6404-ecd3-36b5-a8c211fac774.png">

OAuthなので、「コンシューマ鍵」「コンシューマの秘密」を利用して認証します。Twitter と特定していますので、トークン発行先のURLなどの設定が省略されてますね。ありがたい。Twitter でアプリ登録をして、必要な情報を抜いてくれば、ここで設定するだけでよいということです。これだけで認証連携機能の1ステップが終了することになります。

例えば、「認証プロバイダ」で `Open ID Connect` を利用して、「Google Drive」を利用できるようにした場合はどんな感じになるでしょうか。こんな感じです。

<img width="792" alt="Screen Shot 2019-12-23 at 10.42.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/1bff6923-8087-563c-43ea-1f60626fa9ee.png">

## 「指定ログイン情報」を設定する

先程設定した「認証プロバイダ」を使うもよし、使わず(匿名)もよし、ともかく「指定ログイン情報」を作ります。例えば、swagger API のモックサービスで、認証をろくすぽせずにアクセスできるようにすることが可能です。

<img width="781" alt="Screen Shot 2019-12-23 at 10.35.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/634d7d8a-7b26-72fd-6a65-89bf15392794.png">

そんなときなら、こんな感じだけで作れちゃいます。この場合だと `req.setEndpoint('callout:swagger/some_path');` として`Apex`コールアウトを書くことができます。

認証プロバイダを利用するときには、「ID種別」で「ユーザ」か「指定ユーザ」を選んで、「認証プロトコル」を設定します。

<img width="381" alt="Screen Shot 2019-12-23 at 10.36.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/3c3be8d6-88c5-79dc-f7bd-267ff45ecc58.png">

認証プロトコルで「パスワード認証」や「JWT」を利用する場合は、この「指定ログイン情報」ですべての情報を定義することができ、ここで完結させることができます。「OAuth2.0」を利用するときは、先程の「認証プロバイダ」が必要になります。「保存時に認証フローを開始」としておくと、認証が正しく行われるかの確認を持って、保存してくれるすぐれものです。優しくない？

ここまでやれば、面倒で作りたくない`OAuth2.0`のフローやJWTトークンの管理など、全部おまかせできちゃうわけです。トークンが変わったり、ユーザーのパスワードが変わったとしても、それらは「指定ログイン情報」で管理しておけば、プログラムは一切変更する必要がないのです。

プログラムを開発するメンバーと、運用管理のメンバーをきちんと、正しく分離することができる。素晴らしいですね。Salesforce Platform としての、正しいあり方だと個人的にひしひしと感じます。

## 認証プロバイダで指定できないときは...?

認証プロバイダで指定できない相手のWebサービスが利用できないじゃないか、と思われた方正解です。`Open ID Connect`でない限り、自由なカスタマイズは許可されていません。ある程度の丁寧なOAuth2.0で作られたサイトは、これでもいけるかもしれません。ただ、サーバ向けのAPIサービスで懇切丁寧なOAuth2.0ってことは少なく、他のサービス然りで限定されたIDやシークレット、トークンなどを使っての認証になります。

このようなときは「カスタム認証プロバイダ」を作ります。かつて、弊社のエヴァンジェリストたるひろきくんが[Salesforce に LINE アカウントでログインする方法](https://qiita.com/hrk623/items/942102988a7d8ce08f75)として、カスタム認証プロバイダを利用しています。

あれ、これちょー面倒じゃね？と思われた方、正解です。これについては、私が頑張って解析して汎用的なものを作ったので、別記事として今後公開します。どうぞ、よろしくね。

# おまけ : 外部サービス

指定ログイン情報を作っておくと、あとは「データスキーマ」さえあれば、外部のサービスと連携する`Apexアクション`も作れちゃいます。

`Apexアクション`が作れるということは、フロー、プロセスビルダー、Next Best Action なんかでコードを書かずに再利用することができるということも意味しています。なにげに、やっぱり外部サービス、すごくない？

ちな、さっきの `swagger` 指定ログイン情報を使って、適当に作ってみた Swaggerモックのデータスキーマ(JSON)をそのまま放り込んであげると、このようになります。

<img width="1223" alt="Screen Shot 2019-12-23 at 10.49.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/f34140cc-c722-5938-3952-618f03ddfdb9.png">

フローなどで、この「入力パラメータ」に値を食わせるアクションを作成すると、あら不思議「出力パラメータ」でその内容を再利用できてしまうわけです。このように、外部のWebサービスとの連携の仕組みを「ノーコード」で安全にフローなどに組み込めてしまうこのサービス、知らずに過ごすわけにはいかないですね。