[AWS Amplify] ブラウザからMQTTで(AWS IoT Coreのメッセージブローカーに対して)Publish/Subscribeする

2021.09.25

1 はじめに

IoT 事業部の平内(SIN)です。

AWS Amplify(以下、Amplify)で提供されるJavaScriptライブラリ(PubSubカテゴリ)を使用すると、ブラウザからAWS IoT CoreのメッセージブローカーへのPublish/Subscribeが、簡単に実装できます。

https://docs.amplify.aws/lib/pubsub/getting-started/q/platform/js/

上記のドキュメントでは、Cognitoで認証した状態での利用方法が説明されていますが、個人的に、ちょっと理解が難しかったので、今回、未認証での利用から、認証済み利用への移行という形で、実装方法を確認してみました。(既に、有用なコンテンツがある中、本記事が、個人的な備忘録となっていることをお許しください)

下記は、最終的に認証済みのブラウザと、AWS IoTコンソールのテスト画面でPublish及び、Subscribeの動作を確認している様子です。

2 Authカテゴリの追加

サンプルに使用したプラットフォームは、Vue 3です。

% vue create pub_sub_sample
>Default (Vue 3) ([Vue 3] babel, eslint)

% cd pub_sub_sample
% amplify init

PubSubのライブラリを使用するためには、AWS IoTへのアクセス権付与のため、Authの追加が必須です。

Amplify CLIによる手順は以下の通りとなります。

% amplify add auth
> Default configuration
> Username

% amplify push
% amplify status

    Current Environment: dev

┌──────────┬──────────────────────┬───────────┬───────────────────┐
│ Category │ Resource name        │ Operation │ Provider plugin   │
├──────────┼──────────────────────┼───────────┼───────────────────┤
│ Auth     │ pubsubsampleaf4356ed │ No Change │ awscloudformation │
└──────────┴──────────────────────┴───────────┴───────────────────┘

また、ここでUIコンポーネントのインストールと、リソース情報の受け渡し設定を完了させておきます。

% yarn add aws-amplify @aws-amplify/ui-components

src/main.js

import { createApp } from 'vue'
import App from './App.vue'

// 追加
import Amplify from 'aws-amplify';
import aws_exports from './aws-exports';
import {
  applyPolyfills,
  defineCustomElements,
} from '@aws-amplify/ui-components/loader';

Amplify.configure(aws_exports);
applyPolyfills().then(() => {
  defineCustomElements(window);
});

createApp(App).mount('#app')

3 未認証での利用

(1) ポリシー作成

最初に、my_client_idで接続し、my_topicにPubulish/Subscribeする事ができるポリシーを作成します。

pub_sub_sample_policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Connect"
            ],
            "Resource": [
                "arn:aws:iot:ap-northeast-1:*:client/my_client_id"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish",
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:ap-northeast-1:*:topic/my_topic"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:ap-northeast-1:*:topicfilter/my_topic"
            ]
        }
    ]
}

(2) 未認証アクセスの有効化

Anplifyのコンソールから、セットアップされたCognitoのコンソールを開き、プールIDの編集から、 「認証されていない ID に対してアクセスを有効にする」 にチェックを入れます。

(3) Role(未認証)へのポリシー追加

続いて、未認証で使用されるロールに、先ぽどのポリシー(pub_sub_sample_policy)を追加します。

(4) 動作確認

App.vueを以下のように編集し、PubSub.publish() 及び、PubSub.subscribe() を実装します。

App.vue

<template>
    <h1>Pub/Sub</h1>
    <input type="text" v-model="message" placeholder="メッセージ">
    <button v-on:click="publish">Publish</button>
    <div v-for="item in items" :key="item.message">
      <p>{{ item.message }}</p>
    </div>
</template>

<script>

import Amplify,{ PubSub } from 'aws-amplify';
import { AWSIoTProvider } from '@aws-amplify/pubsub';

const AWS_REGION = 'ap-northeast-1';
const ENDPOINT = 'xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com';
const TOPIC = 'my_topic';

Amplify.addPluggable(new AWSIoTProvider({
  aws_pubsub_region: AWS_REGION,
  aws_pubsub_endpoint: `wss://${ENDPOINT}/mqtt`,
  clientId: 'my_client_id'
}));

export default {
  name: 'App',
  async created() {
    this.subscribe()
  },
  data() {
    return {
      items: []
    }
  },
  methods: {
    async subscribe() {
      PubSub.subscribe(TOPIC).subscribe({
        next: data => {
          console.log(data.value.message)
          this.items = [...this.items, {'message': data.value.message}];
        },
        error: error => console.error(error),
        close: () => console.log('Done'),
      });
    },
    async publish() {
      const { message } = this;
      if (!message) return;

      await PubSub.publish(TOPIC, { message: message });
      this.message = '';
    }
  }
}

</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

input{
  width: 300px;
  height:30px;
}

button {
  margin:10px;
  width: 70px;
  height: 38px;
  border: solid 1px silver;
  border-radius: 0.5rem 0.5rem;
}

button:active{
    padding-top:6px;
    padding-bottom:4px;
    border:1px solid #334c66;
    background-color:#69c;
    color:#ffffff;
}

</style>

Iot Core コンソールのテストクライアントで、メッセージのやり取りを確認できます。ここで分かる通り、未認証で利用する場合、当該ロールに権限を付与するだけで利用できることになります。

(5) 未認証の無効化

未認証でのPublish及び、Subscribeが確認できましたが、続いて、認証時の動作を確認するために、いったん、未認証によるアクセスを無効化します。

無効化した時点で、SubscribePublishは、共にエラーとなって接続できなくなる事が確認できます。

4 認証有りでの利用

(1) 認証画面

認証を有効にするために、App.vuetemplateに、Amplify Framework UIコンポーネント( <amplify-authenticator> 及び、<amplify-sign-out> )を追加します。

src/App.vue

<template>
  <amplify-authenticator>
    <amplify-sign-out></amplify-sign-out>
      <h1>Pub/Sub</h1>
      <input type="text" v-model="message" placeholder="メッセージ">
      <button v-on:click="publish">Publish</button>
      <div v-for="item in items" :key="item.message">
        <p>{{ item.message }}</p>
      </div>
  </amplify-authenticator>
</template>

追加すると、認証されていない場合の画面は、下記のようになります。

Create accountでアカウントを作成し、ログインした状態(認証済み)になると、下記のような画面となります。

(2) Role(認証済み)へのポリシー追加

認証済みの際に有効となるロールに、AWSIoTDataAccess 及び、AWSIoTConfigAccess を追加します。

ここでは、詳細なアクセス制御が行われていませんが、この後で設定する個々のアカウントに対するポリシーで設定することが可能です。

(3) ポリシー作成

今回作成するポリシーは、IoT Coreのポシリーです。内容は、先にIAMで作成したものと同じとなっています。 (名前は、pub_sub_sample_policyとしました)

(4) IDENTITY_IDへのポリシー追加

上記で作成したポリシーは、IDENTITY_IDに紐付けます。

この作業は、コンソールからは行えないため、AWS CLIからの作業になります。

% aws iot attach-policy --policy-name pub_sub_sample_policy --target '<YOUR_COGNITO_IDENTITY_ID>'

なお、IDENTITY_IDは、CognitoのコンソールのIDブラウザで確認できます。現在、1つのアカウントを作成してログインしたので、1つだけ表示されている事が確認できます。

紐付けが完了すると、ポリシーの「証明書」一覧に、表示されます。

(5) 動作確認

ここまでの作業が完了すると、ログインすることで、Pub/Subがエラーなく動作することを確認できます。

5 最後に

今回は、Amplifyによる、PubSubライブラリの利用について確認してみました。個人的には、アカウント単位のIDENTITY_IDにポリシーを紐づけるあたりの作業が、ちょっと理解に苦労しました。

6 参考にさせて頂いたリンク


Cognitoを使ってブラウザアプリからAWS IoTへのPub/Sub権限を制御する