Power Automateを使ってQnA Makerのbotを安全に公開してみる



本記事ではQnA Maker本体の内容には触れません。
QnA Makerそのものの使い方について知りたい方は他の記事をご覧ください。

# やったこと

チャットUIとQnA Makerが直接会話している状態から、
QnA MakerをPower Automateでラップして、チャットUIとPower Automateが会話する状態に変更。
<img width="683" alt="構成.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/b6e9e793-cecb-6164-dfa9-9cc3a6b3d1fb.png">

# 元々の問題点

QnA Makerと会話するには認証情報が必要ですが、インターネット公開しているサイトにチャットUIを公開してまうとこの認証情報もインターネット上にオープンになってしまいます。
認証情報はそうそう変更するわけにもいかないので、オープンにしたくはありません。よね。
この認証情報が盗まれたところで、シナリオが改変されるわけではないですがあまり気持ちの良いものでもないですし。

そんなわけで、認証情報を埋め込んだチャットUIをインターネットに公開してしまうと本来認証情報として使うキーが、単なる識別子としてしか機能しなくなってしまいます。

できれば本来の認証キーは完全隠蔽したいですが、手軽に変更できる第二暗証キーみたいなものがあれば解決できそうですよね。

# 解決策

チャットUIをインターネットに公開する前提が変えられないので、QnA Maker側を隠蔽する方法を考えてみます。
何らかのWebServiceを立てて、そこにAPIを構築すると任意の認証キーを追加することができますね。

とはいえ認証キーを追加するためだけにWebServiceを新規に作るのも面倒。

そんな時にも便利な`Power Automate`。
`要求コネクタ`を使えば、簡単にAPIを作れます。
PREMIUMコネクタなのが痛いところですが。。

# やってみる

解決策の案ができたところで実際にやってみます。

## Power Automateの設定
まずはPower Automateでフローを作成します。

![Power Automateメニュー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/8f62e446-8af8-e0d8-ec63-01ba4f7bbb43.png)
Power Automateのポータルを開き、左側のメニューから`作成`を選択します。

![フローの作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/1032daff-0b91-1140-74b2-57da92472c50.png)
何でもいいですが、`自動フロー`でも選んで最初のダイアログはさくっとスキップして、フローの作成画面へ移動します。

![コネクタの選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/a0fc1f82-235b-3094-4f2e-5b0c89f5ed91.png)
![トリガーの選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/85620b61-f154-308d-15ec-5c653e7893c0.png)
`要求`コネクタを検索して`HTTP要求の受信時`トリガーを選択します。

![要求コネクタの中身](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/d1498518-340b-9bc6-3dcf-1a4cf0d87db4.png)
スキーマを直接入力するか、`サンプルのペイロードを使用してスキーマを生成する`を使ってペイロードからスキーマを生成するか、どちらかでスキーマを定義します。
`HTTP POST の URL`はフローを保存したタイミングで自動で生成されるのでここでは無視しておきます。

```json:サンプルペイロード
{
    "auth":"認証キー",
    "question":"質問"
}
```

```json:スキーマ
{
    "type": "object",
    "properties": {
        "auth": {
            "type": "string"
        },
        "question": {
            "type": "string"
        }
    }
}
```

![条件コネクタの中身](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/73556d22-0d4b-93a8-bd1a-5af95cebed0a.png)
続いて`コントロール`コネクタの`条件`アクションを追加します。
条件に先ほどの`要求`コネクタのスキーマで定義した認証キーが格納される変数`auth`を設定して、値に任意の文字列を設定します。
この任意の文字列が認証キーになります。

![異常応答](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/81df3fbb-3117-764d-e187-c76bc9b63227.png)
認証失敗時（＝認証キーが正しくなかった時）に認証エラーを返すため、先ほどの条件アクションの`いいえ`の方に
`要求`コネクタの`応答`アクションと`コントロール`コネクタの`終了`アクションを追加します。

![QnA Makerコネクタ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/3daf2190-84eb-e2e3-5b28-9a31c8047395.png)
条件コネクタの次に`新しいステップ`を追加して`QnA Maker`コネクタの`Generate answer`アクションを追加して、認証できた場合にQnA Makerにリクエストが飛ぶようにします。

<img width="659" alt="QnAMaker.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/3ca77295-690e-4f2b-e955-ba2b8469f648.png">
QnA MakerのサンプルHTTPリクエストから必要なパラメータをもってきて設定します。
`Question`には`要求`コネクタのスキーマで定義した質問が格納される変数`question`を設定します。

![正常応答](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/c4e31560-d935-0f87-83c4-18c39478945c.png)
最後に`新しいステップ`を追加して`要求`コネクタの`応答`アクションを追加します。
本文にはQnA Makerコネクタの戻り値`Answers`を設定します。
これで完成！

![全体像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72763/51677ddd-1928-0f0a-29a0-199b9fe80549.png)


## チャットUIの変更
チャットUIの接続先やパラメータを先に作っておいたPower Automateのフローのものに変更します。

```javascript:chatUI
const question = ''
fetch(
  'https://***************************************************',
  {
    method: 'POST',
    headers: {
      'Content-Type':'application/json'
    },
    body: '{"question":"' + question + '","auth":"ghqaiwofjsvdnaeuh"}'
  }
).then((res)=>res.json()).then(console.log).catch(console.error)
```

ここで、接続先のURLはPower Automateのトリガーに設定した`要求`コネクタの`HTTP POST の URL`の値を設定します。

# 完成
ここまでの作業をすると、チャットUI側に設定している認証情報は`auth`パラメータの`ghqaiwofjsvdnaeuh`のみになりました。
もし不正なアクセスが発覚したり、何らかの理由で従来のアクセスを制限したくなったりした場合には
Power Automateの`条件`アクションで設定した`auth`の値とチャットUIの`auth`パラメータで設定した`ghqaiwofjsvdnaeuh`部分を別の値に変更すればOK。

副産物として、`auth`の値によって場合分けをすることで接続先のQnA Makerを簡単に変更することもできるようになります。
さらに、`要求`コネクタのスキーマを変更すれば、追加したパラメータで接続先を変更することもできる上に
QnA Makerだけではなく他の何らかのエンジンに要求を振り分けることもできます。
例えば、チャットUI側に`カテゴリ`みたいなものを用意しておき、それによって複数のQnA Makerを使い分けるなんてことも簡単にできます。

Power Automate、やるな。