Node-RED で IBM Cloud App ID をためす 

## 目標
Node-RED で App ID を使ったサインイン処理フローを学ぶ

## 知識材料 
- [Node-RED](https://nodered.jp/about/)
- [IBM Cloud App ID](https://cloud.ibm.com/docs/appid?topic=appid-about)

## 参考ページ

https://qiita.com/yo24/items/09d6b480cb3f033ddd25



## 今回のゴール
IBM Cloud App ID のAPIを使って `OAuth` のフローを体験する

# 実践
## IBM Cloud 設定
### App ID リソース作成
#### カタログから選択
IBM Cloud にログインして、画面上部の **「カタログメニュ」** をクリック後、検索欄に `app` と入力する。
検索欄の下部に  **「App ID」** がサジェスト表示されるのでクリックして選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/ff0d5b5e-a2ad-1e51-0be6-84e5a7991c6b.png)

#### App IDサービスの基本設定
**「ロケーションの選択」** を `東京(jp-tok)` に設定する。

画面右の**サマリー**下部の利用規約に同意して、**「作成ボタン」** をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/7447aa87-ef84-207c-2241-e428e83a4a5f.png)

### App ID テストユーザ作成
作成された  **[App ID リソース]** の設定画面が表示されるので、画面左側のメニューから **「クラウドディレクトリ」** -> **「ユーザー」** を選択する。
画面下部の **「Create User ボタン」** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/2e361ac5-b9b8-d8d8-2887-8b0cbe608139.png)

**[Add a user to test your application ダイアログ]** が表示されるので、`Email:`、`Password:`、`Re-enter Password:` の必須項目を入力して、画面下部の **「Saveボタン」** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/b3a65f34-6a16-59da-ba2d-e728b35de823.png)


### App ID アプリケーション設定
画面左側のメニューから **「アプリケーション」**を選択する。
アプリケーション一覧が表示されるので画面右側の **「Add Applicationボタン」** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/a4a2d378-1179-1c2d-94d0-a5f93265c4c6.png)

**[Add Application ダイアログ]** が表示されるので、**「Name」** に任意の適切な文字列を入力し **「Saveボタン」** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/8b00a567-a449-416f-a4e0-60cdd9567982.png)

作成されたアプリケーションが一覧に表示されるのでクリックして詳細を開き、**下記項目の値をメモ** しておく。
- clientId
- tenantId
- secret
- profilesUrl

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/c6854246-783d-6761-f77b-8b3ad8025ec1.png)

### 認証処理の設定
画面左側のメニューから **「認証の管理」**を選択する。
**[Manage identiry providers]** が表示されるので画面上部の **「Authentication settingsタブ」** をクリックする。
**Add web redirect URLs** 欄に、`{利用を想定しているNode-REDのドメイン:ポート番号}/auth_redirect` を入力し、テキスト入力欄右の **(+)ボタン** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/55df252b-509f-0400-5fb9-793b8e8b3bb3.png)


## Node-RED フロー作成
### フロー作成
下記フローをコピーして Node-RED にインポートする。
```
[{"id":"d76271fd72263d3e","type":"tab","label":"IBM-App-id","disabled":false,"info":"","env":[]},{"id":"1565dfe77fe1efe6","type":"comment","z":"d76271fd72263d3e","name":"open-id-configuration","info":"","x":120,"y":80,"wires":[]},{"id":"96b4611f24117caa","type":"inject","z":"d76271fd72263d3e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":120,"wires":[["d395d7be7c4c3039"]]},{"id":"e5ab70743b6389b6","type":"http request","z":"d76271fd72263d3e","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":490,"y":120,"wires":[["9dadc3a98ca6d324"]]},{"id":"9dadc3a98ca6d324","type":"debug","z":"d76271fd72263d3e","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":660,"y":120,"wires":[]},{"id":"d395d7be7c4c3039","type":"function","z":"d76271fd72263d3e","name":"function 1","func":"const END_POINT = env.get(\"profilesUrl\");\nconst TENANT_ID = env.get(\"tenantId\");\n\nmsg.url = `${END_POINT}/oauth/v4/${TENANT_ID}/` \n          + \".well-known/openid-configuration\";\nmsg.method = \"GET\";\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":120,"wires":[["e5ab70743b6389b6"]]},{"id":"ad89d8a208dc7564","type":"comment","z":"d76271fd72263d3e","name":"authorization","info":"","x":90,"y":200,"wires":[]},{"id":"65a9fff4e36ab576","type":"function","z":"d76271fd72263d3e","name":"RedirectLocation","func":"const END_POINT = env.get(\"profilesUrl\");\nconst TENANT_ID = env.get(\"tenantId\");\nconst CLIENT_ID = env.get(\"clientId\");\n\nconst REDIRECT_URL = env.get(\"redirectUrl\");\n\nconst SCOPE = \"openid\";\n\nmsg.url = `${END_POINT}/oauth/v4/${TENANT_ID}/` \n    + \"authorization\"\n    + \"?\" \n    + \"response_type=code\"\n    + `&client_id=${CLIENT_ID}`\n    + `&redirect_uri=${REDIRECT_URL}`\n    + `&scope=${SCOPE}`;\n\nmsg.headers = {\n    location : msg.url,\n};\nmsg.statusCode = 301;\nmsg.method = \"GET\";\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":240,"wires":[["c05753e6f41dbfb8","11041e2ee2cb90ee"]]},{"id":"3023d7685bd5ed79","type":"http in","z":"d76271fd72263d3e","name":"","url":"auth_redirect","method":"get","upload":false,"swaggerDoc":"","x":130,"y":300,"wires":[["1ccd5e34f70e9132","11041e2ee2cb90ee"]]},{"id":"11041e2ee2cb90ee","type":"debug","z":"d76271fd72263d3e","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":760,"y":300,"wires":[]},{"id":"12d86ab2ae241fcd","type":"http in","z":"d76271fd72263d3e","name":"","url":"/login","method":"get","upload":false,"swaggerDoc":"","x":100,"y":240,"wires":[["65a9fff4e36ab576"]]},{"id":"c05753e6f41dbfb8","type":"http response","z":"d76271fd72263d3e","name":"","statusCode":"","headers":{},"x":650,"y":240,"wires":[]},{"id":"67d3390410bb5b01","type":"http response","z":"d76271fd72263d3e","name":"","statusCode":"","headers":{},"x":750,"y":540,"wires":[]},{"id":"1ccd5e34f70e9132","type":"function","z":"d76271fd72263d3e","name":"GetToken","func":"const END_POINT = env.get(\"profilesUrl\");\nconst TENANT_ID = env.get(\"tenantId\");\nconst CLIENT_ID = env.get(\"clientId\");\nconst SECRET = env.get(\"secret\");\n\nconst REDIRECT_URL = env.get(\"redirectUrl\");\n\nconst bearer_base64 = Buffer.from(`${CLIENT_ID}:${SECRET}`).toString(\"Base64\");\n\nmsg.url = `${END_POINT}/oauth/v4/${TENANT_ID}/` \n    + \"token\";\nmsg.method = \"POST\";\nmsg.headers = {\n    \"Authorization\": `Basic ${bearer_base64}`,\n    \"content-type\" : \"application/x-www-form-urlencoded\"\n};\nmsg.payload = {\n    \"grant_type\": \"authorization_code\",\n    \"redirect_uri\" : REDIRECT_URL,\n    \"code\" : msg.payload.code\n}\n\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":360,"wires":[["2769f444b5411e1b"]]},{"id":"2769f444b5411e1b","type":"http request","z":"d76271fd72263d3e","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":490,"y":360,"wires":[["11041e2ee2cb90ee","3049657267a438da"]]},{"id":"6520a4f9a806cb4b","type":"function","z":"d76271fd72263d3e","name":"decode JWT","func":"var decode_strings = \"\";\nmsg.payload.access_token.split('.').forEach(function(element) {\n    decode_strings += Buffer.from(element, 'base64').toString() + \",\";\n});\n\n\nmsg.payload = decode_strings; //decodeBase64( msg.payload.access_token.split('.')[2]);\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":460,"wires":[["3049657267a438da"]]},{"id":"4fc79d9e499585dc","type":"http request","z":"d76271fd72263d3e","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":510,"y":540,"wires":[["67d3390410bb5b01"]]},{"id":"3049657267a438da","type":"function","z":"d76271fd72263d3e","name":"getUserInfo","func":"const END_POINT = env.get(\"profilesUrl\");\nconst TENANT_ID = env.get(\"tenantId\");\n\nmsg.url = `${END_POINT}/oauth/v4/${TENANT_ID}/` \n    + \"userinfo\";\nmsg.method = \"GET\";\nmsg.headers = {\n    \"Authorization\": `Bearer ${msg.payload.access_token}`,\n};\n\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":540,"wires":[["4fc79d9e499585dc"]]}]
```
### 環境変数の設定
前述の工程でメモした値を Node-RED の環境変数に設定する。

Node-RED 画面右上のメニューから **「設定」**をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/da77b4a9-d0a1-205e-8f32-7144569e6d6e.png)

下記の環境変数を追加する
- clientId
- tenantId
- secret
- profilesUrl
- [ここ](#認証処理の設定)で入力したURL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/ee9fb357-65dd-c61d-05bd-d8a8e997494a.png)


## 実行と結果
以下のURLにアクセスする。
> {利用を想定しているNode-REDのドメイン:ポート番号}/login

ログイン画面が表示されるので[「App ID テストユーザ作成」](#app-id-テストユーザ作成)で作成したユーザの `Email:`、`Password:` を入力して **「Sign inボタン」** をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/0aa3343d-6e4d-92d1-0219-a8001abc6611.png)

サインインに成功すると、ブラウザに[「App ID テストユーザ作成」](#app-id-テストユーザ作成)で作成したユーザ情報が JSON 形式で表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/08e868f4-a3dd-c63e-d146-1c253fa4030f.png)