この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWSなどSalesforceの外部でホストされているアプリケーションをSalesforceから呼び出す方法の一つにCanvasがあります。
Canvas 開発者ガイド
https://developer.salesforce.com/docs/atlas.ja-jp.240.0.platform_connect.meta/platform_connect/canvas_framework_intro.htm
Canvasは署名付きPOST ( Signed Request ) に対応しており、これを使うことで、 Salesforceで認証されたユーザーに対してのみ、目的のアプリケーションに透過的にアクセスさせることが可能です。本エントリーでは、Canvasを使ってAWSでホストされているアプリケーション(以後、Canvasアプリケーションと記述します)にアクセスし、Salesforceのユーザ情報をアプリケーション側で取り出す方法について書きました。
なお、Canvasを扱うためのSDKが提供されています。
- https://github.com/forcedotcom/SalesforceCanvasFrameworkSDK
- https://github.com/forcedotcom/SalesforceCanvasJavascriptSDK
しかし、これらはメンテナンスが数年前から動いていないようで、アーキテクチャも古いため、前出の「Canvas 開発者ガイド」に従って、自前で署名付きPOSTを解析する方法を取りました。
Salesforce内部から外部アプリケーションを安全に呼び出したい方の参考になれば嬉しいです。
SalesforceでCanvasを定義する
Canvasを使用するためには、SalesforceにてCanvas用の接続アプリケーションを定義する必要があります。
接続アプリケーションをCanvas向けに定義する
Salesforceの[設定] > [アプリケーションマネージャ]から[新規接続アプリケーション]をクリックして、Canvas用の接続アプリケーションを作成します。
接続アプリケーションの主な設定は次の通りです。
項目 | 設定値 |
---|---|
接続アプリケーション名 | 任意の値 |
API 参照名 | 任意の値(ここではMY_CANVASとしました) |
OAuth 設定の有効化 | チェックを入れる |
コールバック URL | 適当なURL(ここではCanvasアプリケーションのURLと同じドメインの/callbackパスとしました) |
選択した OAuth 範囲 | 「APIを使用してユーザデータを管理(api)」を選択 |
Web サーバフローの秘密が必要 | チェックを入れる(デフォルトのまま) |
更新トークンフローの秘密が必要 | チェックを入れる(デフォルトのまま) |
キャンバス | チェックを入れる |
キャンバスアプリケーションの URL | CanvasアプリケーションのURL(POST先のURL) |
アクセス方法 | 「署名付き要求(POST)」を選択 |
場所 | 「Visualforce ページ」を選択 |
Canvasアプリケーションを埋め込む場所にVisualforceを選択しましたが、その他の場所も指定できます。
Visualforceから定義した接続アプリケーションを呼び出す
Visualforceから呼び出すためには、定義した接続アプリケーションのAPI参照名を使って次のようにします。
<apex:page>
<apex:canvasApp developerName="MY_CANVAS"
height="1000px" width="800px"
parameters="{p1:'value1',p2:'value2',p3:'value3'}"/>
</apex:page>
developerNameにAPI参照名を渡します。
また、parametersにJSON形式でCanvasアプリケーションに渡すパラメータを指定することができます。
外部でホストされるアプリケーション(Canvasアプリケーション)を用意する
POST先のCanvasアプリケーションを用意します。ここでは簡単にAWS Amplifyで用意することにしました。
AWS Amplifyで署名付きPOST先のCanvasアプリケーションを用意する
Canvasアプリケーションのアプリ名をsample-canvas
としました。ここではReactを使っています。
$ npx create-react-app sample-canvas
$ cd sample-canvas
Amplifyプロジェクトを立ち上げて初期化します。
$ amplify init
? Enter a name for the environment dev
? Choose your default editor: Vim (via Terminal, macOS only)
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path: src
? Distribution Directory Path: build
? Build Command: npm run-script build
? Start Command: npm run-script start
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
? Please choose the profile you want to use [select your profile]
Lambda functionを追加します。関数名はhello
としました。
$ amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: hello
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World
Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration
- Environment variables configuration
- Secret values configuration
? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? No
定義したLambda functionにアクセスするAPI Gatewayを追加します。API名もhello
としました。
$ amplify add api
? Select from one of the below mentioned services: REST
✔ Would you like to add a new path to an existing REST API: (y/N) · no
✔ Provide a friendly name for your resource to be used as a label for this category in the project: · hello
✔ Provide a path (e.g., /book/{isbn}): · /hello
✔ Choose a Lambda source · Use a Lambda function already added in the current Amplify project
✔ Choose the Lambda function to invoke by this path · hello
✔ Restrict API access? (Y/n) · no
✔ Do you want to add another path? (y/N) · no
Restrict API access?
をnoにしていますが、本番運用する場合はYesにしてください。
定義したリソースをデプロイします。
$ amplify publish
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/helloのようなAPI GatewayのURLが生成されます。
生成されたAPI GatewayのURLはSalesforceのCanvasアプリケーション設定のキャンバスアプリケーションの URL
に忘れずに設定してください。
署名付きPOSTをLambdaで解析して、Salesforceユーザー情報を取り出す
ここまでで、作成したVisualforceページにアクセスすると、CanvasによってAmplifyで作成したCanvasアプリケーションが認証付きPOSTでコールされるようになっています。あとは、Canvasアプリケーション側で認証付きPOSTを解析して、Salesforceから渡される各種情報を取り出します。
具体的にはAmplifyで作成したLambda function(amplify/backend/function/hello/src/index.js
)を次のように書き換えます。
import crypto from 'crypto';
function decode(signed_request, secret) {
if( !signed_request || !secret ) {
throw new Error('Must pass both signed_request and api secret');
}
// decode the data
let data, encoded_data, sig;
try {
encoded_data = signed_request.split('.', 2);
sig = encoded_data[0];
const json = Buffer.from(encoded_data[1], 'base64').toString();
data = JSON.parse(json);
} catch ( e ) {
throw new Error('Could not parse signed-request');
}
// check algorithm
if ( !data.algorithm || data.algorithm.toUpperCase() !== 'HMACSHA256' ) {
throw new Error('Unknown algorithm. Expected HMACSHA256');
}
// check sign
const expected_sig = crypto.createHmac('sha256', secret).update(encoded_data[1]).digest('base64');
if ( sig !== expected_sig ) {
throw new Error('Bad signed JSON Signature!');
}
return data;
}
export async function handler(event) {
try {
const signed_req = decodeURIComponent(event.body.split('=', 2)[1]);
const body = decode(signed_req, '<接続アプリケーションの[コンシューマの秘密]を指定する>');
return {
statusCode: 200,
body: JSON.stringify(body.context.user)
};
} catch ( e ) {
console.error(e);
}
};
POSTのbodyにsigned_request=署名付きPOSTの値
というフォーマットで署名付きPOSTの値が送られてくるので、これを取り出して、署名付き要求の検証および復号化のドキュメントにならって、リクエストの検証と復号化をdecode関数で行なっています。
最初にdecodeURIComponent
でリクエストをURIデコードする必要があることに留意してください(これでめちゃくちゃ嵌りました)。
最後に、body.context.user
でSalesforceのユーザ情報を取り出して返しています。そのほかの情報も取り出せますので、詳しくは次のリファレンスを参照してください。
ユーザ情報にはロールID(roleId)なども含まれているので、ユーザのロールによってCanvasアプリケーション側の挙動を変える、といったことも可能です。
署名付きPOSTを検証することで、確実にCanvasを定義したSalesforce組織からのリクエストであることを保証できています。 作成したVisualforceページにアクセスして、Salesforceのユーザ情報(今、ログインしている自身のSalesforceのユーザ情報)がJSONで返ってきていれば成功です。
まとめ
SalesforceのCanvasを用いて、外部でホストされているアプケーションをSalesforce内部から安全に呼び出す方法について書きました。署名付き要求の取り扱い方の参考になれば幸いです。
Canvasを活用することで、Salesforceの認証を引き継ぎつつ、複雑な画面機能はAWSなどのSalesforce外部の仕組みを使って実装することが可能になります。アプリケーションの表現力が強力になりますので、検討してみてください。