ちょっと話題の記事

[日本語Alexa] Cognito UserPoolでアカウントを管理して特定のデバイスの情報にアクセスする

2019.06.07

1 はじめに

AIソリューション部の平内(SIN)です。

今回は、AWS IoT経由でAWS上にデータを保存(今回は、DynamoDBを想定)するデバイスがあった時、その情報を再生するスキルを作成してみました。

利用者は、ユーザー登録を行うことで、手元のデバイスを識別するID(ここではデバイスIDと表現)を自分のアカウントに紐付けるイメージです。

アカウントリンクを使用すると、強制的にログインが必要になり、今回使用した、Cognito User Poolでは、サインアップ機能も有効にできるので、スキルを有効化するタイミングでユーザー登録(作成)が可能です。

デバイスIDをユーザーの登録の必須項目としておくことで、「ユーザー登録」=「デバイスID紐づけ」という仕組みが作れることになります。

2 構成

構成については、概ね以下のとおりです。

  • ① IoT経由でデバイス固有のIDをキーにして、DynamoDBのデータを更新するデバイスがあります。(想定)
  • ② スキルは、有効にするためにアカウントリンクが必要です。
  • ③ アカウントリンク時にCognito User Poolでユーザーを新規に作成します。
  • ④ この時、ユーザーは、デバイスのIDを入力します。
  • ⑤ スキルから呼び出さ出されたLambdaでは、アクセストークンでデバイスIDを取得し、DynamoDBにアクセスします。

※ DynamoDBへのパーミッションは、RoleでLambdaへ付与されています。Cognito User Poolは、OAuth2サーバとしてのみ機能しています。

3 Cognito UserPoll

OAuth2サーバとして利用するため、Cognito User Poolを使用します。
参考:[日本語Alexa] Cognito User Pool によるアカウントリンク

必須属性にemai及び、profileの2つを設定しました。このprofile属性にデバイスIDを格納します。

アプリクライアントは、クライアントシークレット生成を有効にして作成します。

ドメインは、利用可能な名前に制限がありますが、ここでは、alexa-user-poolとしました。

アプリクライアントの設定は、以下のとおりです。

  • 有効なIDプロパイダ: Cognito User Pool
  • コールバックURL: スキルのアカウントリンクの設定画面からコピーします
  • 許可まれているOAuthフロー: Authoraization code grant
  • 認可されているOAuthスコープ: openid

4 アカウントリンク設定

スキル側のアカウントリンクの設定は、以下のとおりです。

  • Authorization Grant種別: Auth Code Grant (アプリの設定と整合させます)
  • 認証画面のURL https://ユーザープールドメイン名/oauth2/authorize
  • アクセストークンURL https://ユーザープールドメイン名/oauth2/token
  • クライアントID: アプリの アプリクライントID (Cognito User Pool)
  • クライアントシークレット: ID アプリのシークレット (Cognito User Pool)
  • クライアントの認可方式: HTTP Basic認証(推奨)
  • スコープ: openid

5 スキル有効化 / アカウント作成

スキルの有効化から、ユーザー登録までの流れは以下のとおりです。

  • スキルを有効化します。

  • ユーザID・パスワードに認証画面となるので、はじめての場合は、新規登録(Sign up)を選択します。

  • Sign up時は、名前・パスワード。メールアドレスの他に、Profileの入力が求められますので、ここにデバイスIDを入力します。(注:図では、DEV12345となっていますが、テストは、1234567で行いました)

  • 指定したメールアドレスに認証コードが送られて来ますので、これで認証します。

  • 「正常にリンクされました」が表示されたら完了です。

  • Cognito User Poolでも、ユーザーが追加されていることを確認できます。

6 スキルの実装

アカウントリンクが完了したスキルには、アクセストークンが送られてきますので、それを使用して、Cognito User PoolのAPIでProfile属性を取得します。

また、Profile属性をキーとしてDynamoDBからデータを取得します。

DynamoDBに保存されている情報は以下のようなものです。

コードは、以下のとおりです。

const LaunchRequestHandler = {
    canHandle(h) {
        return h.requestEnvelope.request.type === 'LaunchRequest';
    },
    async handle(h) {
        // User Poolから情報を取得する
        const domain = 'alexa-user-pool';
        const url = 'https://' + domain + '.auth.ap-northeast-1.amazoncognito.com/oauth2/userInfo';
        const accessToken = h.requestEnvelope.session.user.accessToken;
        const options = {
            uri: url,
            method: "GET",
            headers: { Authorization: 'Bearer ' + accessToken },
        }
        const id = JSON.parse(await rp(options)).profile;

        // DynamoDBからデバイスIDに基づいたデータを検索する
        const data = await getDynamoDb('alexa-sample', id)
        const counter = data.Item.counter;

        const speechText = 'デバイスのカウンターは、' + counter + ' です。';
        return h.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
    }
};

async function getDynamoDb(tableName, id) {
    var docClient = new AWS.DynamoDB.DocumentClient();
    var params = {
        TableName: tableName,
        Key:{
            "device-id": id
        }
    };
    return await docClient.get(params).promise();
}

スキルからは、以下のようメッセージが返されるはずです。

User: アレクサ、***をスタートしてして
Alexa: デバイスのカウンターは、302です

7 最後に

今回は、OAuth2サーバを使用し、デバイスとスキルの紐づけを試してみました。

最初に、書いたとおりCognitoは、あくまで、ユーザー管理(+Auth2サーバ)として使用しているので、ユーザー管理ができるOAuth2サービス(任意の属性が保持できる)があれば、何でも同じような仕組みが作れると思います。


弊社では、Amazon Connectに関するキャンペーンを行なっています。
【6/27(木)東京】「1時間でクラウド型コンタクトセンターを構築できるようになる!無料Amazon Connectハンズオンセミナー」を開催します

また、音声を中心とした各種ソリューションの開発支援を行なっております。