8
4

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 5 years have passed since last update.

Salesforce で Client Credentials フローな指定ログイン情報を使いたい

Posted at

まだコロナの影響もなく、騒いでいることのできた昨年末のAdvent Calendarで「Salesforce で外部サービスを使うときには「指定ログイン情報」を使いましょう」と偉そうに書いてから、もう4ヶ月近くが経過します。

確かに「指定ログイン情報」は便利です。Apexコールアウトするときも、「外部サービス」を利用するときも指定ログイン情報様様です。

しかし、その便利な「指定ログイン情報」も、「認証プロバイダ」があってこそです。いま対応しているプロバイダは次の10個です。

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

※ 2020/4/20 現在 (Apple増えたね)

これを見て、心穏やかにならないなにかがありませんか?そうです、「認可」として業界標準で最も利用されている「OAuth2.0」がありません。TwitterやFacebookだってOAuthアクセスなのに、なぜ標準的なOAuth2.0は登録されていないのか。Open ID Connectはあるのに...。

と気が滅入った方。多分仕方ないです。OAuth2.0はその認可の方法が多岐にわたり、一つ一つを登録していく労力やらを考えると、利用者の多いサイトを認証プロバイダとして登録するほうがサービサー的には正しいかもしれません。

しかし、我々は OAuth2.0 で認可されているサイトを外部サービスとして利用したいんです。さて、どうするべきか。

この方法は、われらがデベロッパエヴァンジェリストである@hrk623の書いた「netatmo Weather Station のデータを定期的に Salesforce に取り込んでみた」のように、長い手順を踏んだ上に「カスタム認証プロバイダ」をapexで記述する必要があります。

なんてこった。

Salesforce はAdmin利用の多いサービスであり、ノーコード・ローコードが売りです。この部分なんとかならんかと思い、ここでわたしは一肌脱いだ次第です(前置きが長い)。

Client Credentials をかんたんに実装しよう

そこで、外部サービスとしてシステム連携を行う際に利用する頻度の高いと思われる「Client Credentials」について、できるだけかんたんに実装できる「ソースコード」を準備しました。しかも、カスタマイズ機能付き。

今日は、こちらの使い方を紹介します。Apexコードの解説は別途。

Client Credential フローって?

先程のauth0サイトのドキュメントが詳しいのですが、事前に認証されて取得した次の2つを利用して(1)、認可用のアクセストークンを発行してもらい(2),(3)、APIサーバへはそのアクセストークンを使ってアクセスする方式(4),(5)です。システム連携用としては、非常に多いパターンです。認証スキーム名としてBearerを利用する方式ですね。

  1. Client ID
  2. Client Secret

auth-sequence-client-credentials.png
※ from https://auth0.com/docs/flows/concepts/client-credentials

使い方

Github 上のソースコードに記載のあるとおりの手順を実行すれば、DevHub内にスクラッチ組織を作成して、そこへ「Client Credentials」用のカスタム外部認証プロバイダを登録します。

流石に投げっぱなしすぎるので、一つずつ手順と注意事項を記載しておきます。

前提条件

  • 組織でDevHubを利用可能としている (その他の環境へDeployする場合は不要です)
  • Salesforce CLI をインストールしていること
  • git コマンドが利用できること

ソースコードを準備します

Githubのソースコードを、git clone して入手します。

git clone https://github.com/sho7650/clientCredentialsAuthProvider.git
cd ./clientCredentialsAuthProvider

ls -latrすると、こんな感じのディレクトリ構成になっていれば問題なしです。

$ ls -latr
total 56
drwxr-xr-x   3 sho  staff    96 Apr 20 10:40 ../
-rwxr-xr-x   1 sho  staff   349 Apr 20 10:40 .forceignore*
-rwxr-xr-x   1 sho  staff   597 Apr 20 10:40 .gitignore*
-rwxr-xr-x   1 sho  staff   155 Apr 20 10:40 .prettierignore*
-rwxr-xr-x   1 sho  staff   228 Apr 20 10:40 .prettierrc*
-rwxr-xr-x   1 sho  staff  4206 Apr 20 10:40 README.md*
drwxr-xr-x   4 sho  staff   128 Apr 20 10:40 bin/
drwxr-xr-x   3 sho  staff    96 Apr 20 10:40 config/
drwxr-xr-x   4 sho  staff   128 Apr 20 10:40 force-app/
drwxr-xr-x   3 sho  staff    96 Apr 20 10:40 manifest/
-rwxr-xr-x   1 sho  staff   193 Apr 20 10:40 sfdx-project.json*
drwxr-xr-x  12 sho  staff   384 Apr 20 10:40 .git/
drwxr-xr-x  14 sho  staff   448 Apr 20 10:41 ./
drwxr-xr-x   5 sho  staff   160 Apr 20 10:44 .sfdx/

Salesforce 組織の認証

DevHub を利用する場合は次のとおりに行います。

  • ... 使用するDevHubのエイリアス(別名)をつけます。使いやすいようにです
  • ... deploy先のスクラッチ組織のエイリアス(別名)をつけます。同じく使いやすくするためです
sfdx force:auth:web:login -d -a <alias your DevHub>
sfdx force:org:create -s -f config/project-scratch-def.json -a <alias your scratch Org>

sfdx force:auth:web:login すると Salesforce組織へWebベースフローで認証するため、Salesforce のログインが出てきます。ここでDevHubのある組織へログインします。次の sfdx force:org:create コマンドにてスクラッチ組織を作成します。

もしここで、スクラッチ組織ではなくログイン先のDevloper、Sandbox、本番環境へdeployする場合にはスクラッチ組織を作らなければdeploy可能です(多分)。

ソースコードのpushとテスト

では、手元にあるソースコードを Salesforce へ push(アップロード) しましょう。

sfdx force:source:push

何事もなければ、次のようにファイルがすべて登録されます。

=== Pushed Source
STATE  FULL NAME                                                          TYPE               PROJECT PATH
─────  ─────────────────────────────────────────────────────────────────  ─────────────────  ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Add    ClientCredentials                                                  AuthProvider       force-app/main/default/authproviders/ClientCredentials.authprovider-meta.xml
Add    ClientCredentialsAuthProvider                                      ApexClass          force-app/main/default/classes/ClientCredentialsAuthProvider.cls
Add    ClientCredentialsAuthProvider                                      ApexClass          force-app/main/default/classes/ClientCredentialsAuthProvider.cls-meta.xml
Add    ClientCredentials_Metadata.ClientCredentials                       CustomMetadata     force-app/main/default/customMetadata/ClientCredentials_Metadata.ClientCredentials.md-meta.xml
Add    ClientCredentials_Metadata__mdt-ClientCredentials_Metadata Layout  Layout             force-app/main/default/layouts/ClientCredentials_Metadata__mdt-ClientCredentials_Metadata Layout.layout-meta.xml
Add    ClientCredentials                                                  NamedCredential    force-app/main/default/namedCredentials/ClientCredentials.namedCredential-meta.xml
Add    ClientCredentials_Metadata__mdt                                    CustomObject       force-app/main/default/objects/ClientCredentials_Metadata__mdt/ClientCredentials_Metadata__mdt.object-meta.xml
Add    ClientCredentials_Metadata__mdt.Access_Token_URL__c                CustomField        force-app/main/default/objects/ClientCredentials_Metadata__mdt/fields/Access_Token_URL__c.field-meta.xml
Add    ClientCredentials_Metadata__mdt.Auth_Provider__c                   CustomField        force-app/main/default/objects/ClientCredentials_Metadata__mdt/fields/Auth_Provider__c.field-meta.xml
Add    ClientCredentials_Metadata__mdt.Client_ID__c                       CustomField        force-app/main/default/objects/ClientCredentials_Metadata__mdt/fields/Client_ID__c.field-meta.xml
Add    ClientCredentials_Metadata__mdt.Client_Secret__c                   CustomField        force-app/main/default/objects/ClientCredentials_Metadata__mdt/fields/Client_Secret__c.field-meta.xml
Add    ClientCredentials_Metadata__mdt.User_Name__c                       CustomField        force-app/main/default/objects/ClientCredentials_Metadata__mdt/fields/User_Name__c.field-meta.xml
Add    clientCredentials                                                  RemoteSiteSetting  force-app/main/default/remoteSiteSettings/clientCredentials.remoteSite-meta.xml
Add    ClientCredentialsAuthProviderTest                                  ApexClass          force-app/test/default/classes/ClientCredentialsAuthProviderTest.cls
Add    ClientCredentialsAuthProviderTest                                  ApexClass          force-app/test/default/classes/ClientCredentialsAuthProviderTest.cls-meta.xml

無事に登録された場合には、テストも可能です。テストコードも実行してみましょう。

sfdx force:apex:test:run --tests ClientCredentialsAuthProviderTest --resultformat human --outputdir .sfdx/tools/testresults/apex --loglevel error --codecoverage

全体で6つのテストケースがありますので、問題がなければすべて次のように全てパスします。

=== Test Reports
FORMAT  FILE
──────  ──────────────────────────────────────────────────────────────────
txt     .sfdx/tools/testresults/apex/test-result.txt
txt     .sfdx/tools/testresults/apex/test-run-id.txt
junit   .sfdx/tools/testresults/apex/test-result-7075D000018Sj4b-junit.xml
json    .sfdx/tools/testresults/apex/test-result-7075D000018Sj4b.json
json    .sfdx/tools/testresults/apex/test-result-codecoverage.json

=== Apex Code Coverage
ID                  NAME                           % COVERED  UNCOVERED LINES
──────────────────  ─────────────────────────────  ─────────  ───────────────
01p5D0000013JboQAE  ClientCredentialsAuthProvider  100%

=== Test Results
TEST NAME                                                    OUTCOME  MESSAGE  RUNTIME (MS)
───────────────────────────────────────────────────────────  ───────  ───────  ────────────
ClientCredentialsAuthProviderTest.getCustomMetadataTypeTest  Pass              23
ClientCredentialsAuthProviderTest.getUserInfoTest            Pass              6
ClientCredentialsAuthProviderTest.handleCallbackErrorTest    Pass              6
ClientCredentialsAuthProviderTest.handleCallbackTest         Pass              5
ClientCredentialsAuthProviderTest.initiateTest               Pass              5
ClientCredentialsAuthProviderTest.refreshTest                Pass              5

=== Test Summary
NAME                 VALUE
───────────────────  ──────────────────────────────────────────────────────────
Outcome              Passed
Tests Ran            6
Passing              6
Failing              0
Skipped              0
Pass Rate            100%
Fail Rate            0%
Test Start Time      Apr 20, 2020 10:44 AM
Test Execution Time  50 ms
Test Total Time      50 ms
Command Time         1783 ms
Hostname             https://java-business-3629-dev-ed.cs72.my.salesforce.com/
Org Id               00D5D000000DRVEUA4
Username             test-clientcredentials@example.com
Test Run Id          7075D000018Sj4b
User Id              0055D000003BGPDQA4
Test Run Coverage    100%
Org Wide Coverage    100%

これで、コードは登録できましたので、あとは次の2つを相手の認証・認可サーバに必要な設定を行えば設定完了です。

どうせなら、カスタマイズしたままdeployしたい

そうでしょう。そうでしょう。どうせなら、コードをpushした時にすべての構成が反映している方が望ましいです。分かります、その気持ち。

はい、準備しました。次のコマンドを実行するだけです。

./bin/config.sh <your config>

※ macOSかLinuxで実行すること。WindowsユーザはWSL上で実行してください

とは言え、このままだと何も起きません、残念な結果になります。まずは設定ファイルを準備しましょう。

次のようなファイルを作成します。.config というファイル名にすれば設定ファイル名を指定しなくとも自動的に読み込みます。

ENDPOINT='https://test.example.com'
EXECUTION_USER='test@example.com'
REMOTE_SITE_URL='https://api.example.com'
ACCESS_TOKEN_URL='https://example.com/token'
CLIENT_ID='client_id'
CLIENT_SECRET='client_secret'
環境変数名 内容 デフォルト値
ENDPOINT 指定ログイン情報で指定するコールアウト先のエンドポイント https://test.example.com
EXECUTION_USER 認証プロバイダに登録される実行ユーザ名。実際に登録されているシステムユーザを指定しましょう test-clientcredentials@example.com
REMOTE_SITE_URL リモートサイトへ登録するリモートサイトのホスト名 https://api.example.com
ACCESS_TOKEN_URL カスタムメタデータ型に登録されているアクセストークンを発行するリモートサイトのURL https://example.com/token
CLIENT_ID カスタムメタデータ型 に登録されている、外部サービスから発行された Client_ID client_id
CLIENT_SECRET カスタムメタデータ型に登録されている、外部サービスから発行されたClient_SECRET client_secret

これらを指定したファイルを準備して、./bin/config.sh を実行すると該当のソースコード部分が全て置き換わります。書き換え後の情報は機密情報になるので、そのまま github などのオープンリポジトリへはpushしないように気をつけましょう。

それでは、より良いハック人生を。

8
4
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
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?