Node-REDは主にIoT領域でサービス連携に使われてますが、Node-RED を使用してリアルタイムのチャット・アプリケーションを 5 分で作成するのようにWebページ/アプリケーションを作ることも可能です。
ということは、SalesforceのCanvasを使えばNode-REDで作成したWebアプリケーションがSalesforce内で使える!ということで、このたびnode-red-contrib-forceにSigned Requestを検証&デコードするノードを作りました。
以下、チュートリアル的に紹介します。
Node-REDをHerokuで動かす
Node-REDをHerokuで動かすには以下のHerokuボタンをクリックしてください(以下、例として https://node-red-123.herokuapp.com にデプロイしたものとします)
何もしないとNode-REDのエディタ画面がフルオープンになってしまうのでHerokuの環境変数としてNODE_RED_USERNAME
とNODE_RED_PASSWORD
を必須としてます。ユーザ名とパスワードを決めて入力したら "Deploy for Free" ボタンをクリックしてください。

デプロイしたらホストにアクセスしてみます(今回の例ですと https://node-red-123.herokuapp.com )以下のような画面が表示されますので "Go to your Node-RED flow editor" ボタンをクリックします。

するとログイン画面に遷移しますので先ほどHerokuデプロイ画面で設定したユーザ名とパスワードを入力してログインします。

これで以下のようにNode-REDエディタ画面が表示されればOKです。今後、直接エディタ画面を開くには/red
を付与してアクセスします(今回の例ですと https://node-red-123.herokuapp.com/red )

あとは今回紹介するnode-red-contrib-forceというモジュール/プラグイン的なもの(Node-REDでは一つ一つのモジュール/プラグイン的なものをノードと呼びます)をインストールします。
node-red-contrib-forceを使うには、npm install -g node-red-admin
でnode-red-adminをインストールして以下のコマンドを叩きます。
$ node-red-admin target <Node-REDのエディタ画面のURL 例:https://node-red-123.herokuapp.com/red>
$ node-red-admin login
Username: <Herokuにデプロイする時に設定したユーザ名>
Password: <Herokuにデプロイする時に設定したパスワード>
Logged in
$ node-red-admin install node-red-contrib-force
Module: node-red-contrib-force
Version: 0.0.6
Nodes Types State
node-red-contrib-force/chatter force-chatter in enabled
node-red-contrib-force/forcedotcom force enabled
force in
node-red-contrib-force/forcedotcom-bulk force-bulk in enabled
node-red-contrib-force/forcedotcom-signed-request force signed request enabled
Node-REDでWebページを作成する
まず、以下のようにHTTP Inputノードをドラッグ&ドロップしてワークスペースの好きな場所へ置きます。

次に配置したノードをダブルクリックするとノード定義用の設定ダイアログが開きますので以下のようにHTTP Inputノードに/canvas
というURLを設定します。これだけでNode-REDが動くHTTPのGETメソッドのエンドポイントを作成できます。

あとは表示するWebページを定義するTemplateノードとHTTP Outputノード(HTTPレスポンス)と念のためDebugノードを以下のように配置してDeployボタンをクリックします。上記でエンドポイントを定義したHTTP Inputノード以外はデフォルトのまま(配置してつなぐだけ)で良いです。

ここまでできたところでNode-REDをデプロイしたホストにHTTP Inputノードで定義したエンドポイント(今回の例ですと https://node-red-123.herokuapp.com/canvas )へアクセスしてみます。

Templateノードがデフォルト定義のままなので[object Object]
となってますがWebページが返ってきました。
続いて先ほどのURLに?aaa=111
というパラメータを付与して(今回の例ですと https://node-red-123.herokuapp.com/canvas?aaa=111 )アクセスしてみます。
表示されるWebページは変化ありませんがNode-REDのエディタ画面に戻るとデバッグに送信したパラメータが表示されています。

これはHTTP Inputノード([get] /canvas
)で受け取ったパラメータをNode-REDがJSONに変換してmsg.payload
というプロパティにセットしてDebugノードへ流した結果です。

これでNode-REDのノード同士を接続するラインを流れるメインのデータはmsg.payload
を流れているのが解るのでTemplateノードを以下のように変更してみます(Templateノードをダブルクリックすると設定ダイアログが開きます)
This is the payload: {{payload.aaa}} !

これで再度デプロイします。

そして再度パラメータ付きでアクセスしてみますと(今回の例ですと https://node-red-123.herokuapp.com/canvas?aaa=111 )以下のようにパラメータの値がWebページ表示されます。あとは "111" を他の値に変えたりして仕組みを理解しましょう。

Salesforce Canvasに表示する
さて、前置きが長くなりましたが本題です。
まず接続アプリケーションの作成ですが基本的なSalesforce Canvasの設定についてはSFDC:Force.com CanvasでHerokuアプリと連携してみましたが詳しいです。
あえて補足しますと、以下のように "キャンバスアプリケーションの URL" に作成したエンドポイント(今回の例ですと https://node-red-123.herokuapp.com/canvas )アクセス方法を "署名付き要求(POST)" 、場所に "Visualforce ページ" を選択します。

接続アプリケーションのアクセス方法を "署名付き要求(POST)" に設定したので以下のようにNode-REDのエンドポイントもPOSTメソッドを受け付けるように変更します。

続いて以下のようにノードを配置してforce signed requestノードをダブルクリックして設定ダイアログを開きます。

ここでSalesforceと接続する情報を設定するために以下のようにペンマークのアイコンをクリックします。

以下のように "Login Type" は "login By Signed-Request" を選択して "User Name" に組織のユーザ名と "Client Secret" に接続アプリケーションの "コンシューマの秘密" を入力して "Add" ボタンをクリックします。

ちなみに、ここで設定した組織のユーザ名は単なる目印なので以下のようにメールアドレス形式でもないものでも構いません。これで "OK" ボタンをクリックしてして設定ダイアログを閉じて "Deploy" ボタンをクリックしてデプロイします。

最後にVisualforceページを用意します。詳しく説明しませんが接続アプリケーションが "test" という名前の場合は以下のような感じです。
<apex:page >
<apex:canvasApp developerName="test" />
</apex:page>

これでプレビューを見てみましょう。以下のようにSalesforceから送られてきたSigned Requestが検証&デコードされてコンテキストがJSONとなって画面とNode-REDのデバッグに表示されればOKです。


Salesforceのデータを取得して画面にリスト表示する
これでSalesforce CanvasのSigned Requestでの認証はうまくいったので、このままNode-REDでSalesforce側のデータを取得してTemplateノードで表示してみましょう。
まずは以下のようにChangeノードを配置します。

配置したChangeノードの設定ダイアログを開き(Changeノードをダブルクリック)以下のようにmsg.payload
へSelect Id, Name From Account
というSOQL文字列をセットするように定義します。

続いて以下のようにforceノードを配置します。

配置したforceノードの設定ダイアログを開き(forceノードをダブルクリック)以下のように "Log in as" は先ほどforce signed requestノードで設定したアカウントと同じアカウントを選択、 "Operation" は "query" を選択してデプロイします。

これでSalesforceからデータ取得できるか一旦試してみます。再度、先ほど作成したVisualforceページへアクセスします。以下のようにJSONでAccountの情報が表示されればOKです。

これはChangeノードでSOQLをmsg.payload
にセットして流すことで、後続のforceノードが受け取ったSOQLを実行してデータを取得しているという流れです。
Node-REDでは前段のノードから後続ノードのパラメータを流して、後続の動作を動的にするテクニックをよく使います。これによって冗長なデータフローになるのを避けられます。
最後にTemplateノードを以下のように配置して...

以下のように定義してデプロイして...
<ul>{{#payload.records}}<li>{{Id}} : {{Name}}</li>{{/payload.records}}</ul>

再度、Visualforceページへアクセスして以下のように表示されたらOKです。

まとめ
私が思うにNode-REDはSalesforceの外の世界でもビジュアルワークフロー&データフロー的なプロトタイピングでユーザと会話しながら要件の認識を合わせていくのに適したツールだと思います。
そのような使い方を可能にするためには気軽に誰でもどこでもインストールして使えるべきです。オープンソースソフトウェアの時点で、その大半はクリアしているのですが、今後も色々と考慮して発展していかないといけない点も多々あります。
そのためには様々な領域の有志が集い、様々なユースケースにおいてNode-REDを利用するにはどうしたら使いやすいかを考え発展させなければならないと思います。また、Node-REDは発展できるポテンシャルもあります。
貢献してみたい方は是非Node-REDユーザグループに参加しましょう!
おまけ
今回の設定(Node-REDではフローと呼びます)はJSONをインポートすると再現できます。インポートのやり方は以下のようにメニューから "Import > Clipboard" を選択して開いたダイアログに以下のJSONを貼付けます。

[{
"id": "4f9b24d7.b064dc",
"type": "force",
"z": "",
"username": "",
"loginurl": "https://login.salesforce.com",
"logintype": "Signed-Request"
}, {
"id": "68be3f5.f9741c",
"type": "debug",
"z": "569fcd48.a96034",
"name": "",
"active": true,
"console": "false",
"complete": "false",
"x": 513,
"y": 222,
"wires": []
}, {
"id": "d1e76ece.2e189",
"type": "http in",
"z": "569fcd48.a96034",
"name": "",
"url": "/canvas",
"method": "post",
"swaggerDoc": "",
"x": 125,
"y": 63,
"wires": [
["f70ef5e8.08f108"]
]
}, {
"id": "f70ef5e8.08f108",
"type": "force signed request",
"z": "569fcd48.a96034",
"force": "4f9b24d7.b064dc",
"name": "",
"x": 320,
"y": 64,
"wires": [
["870ac031.78f54"]
]
}, {
"id": "870ac031.78f54",
"type": "change",
"z": "569fcd48.a96034",
"name": "",
"rules": [{
"t": "set",
"p": "payload",
"to": "Select Id, Name From Account"
}],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 526,
"y": 64,
"wires": [
["9238e368.6dc72"]
]
}, {
"id": "9238e368.6dc72",
"type": "force in",
"z": "569fcd48.a96034",
"force": "4f9b24d7.b064dc",
"operation": "query",
"sobject": "",
"extname": "",
"name": "",
"x": 128,
"y": 135,
"wires": [
["68be3f5.f9741c", "7612ac7f.89ed54"]
]
}, {
"id": "5e74439f.a18bbc",
"type": "http response",
"z": "569fcd48.a96034",
"name": "",
"x": 494,
"y": 135,
"wires": []
}, {
"id": "7612ac7f.89ed54",
"type": "template",
"z": "569fcd48.a96034",
"name": "",
"field": "payload",
"format": "handlebars",
"template": "<ul>{{#payload.records}}<li>{{Id}} : {{Name}}</li>{{/payload.records}}</ul>",
"x": 310,
"y": 137,
"wires": [
["5e74439f.a18bbc"]
]
}]