Alexaスキル、Amazon Cognito ユーザープール (Login with Amazon) を使ったアカウントリンクを実装し、スキル内でアカウント情報を取得する

# はじめに

アカウントリンクを使用したAlexaスキルを作成しようと思い、`Amazon Cognito` のユーザープールに、`Login with Amazon` を紐づけたアカウントリンクをやってみようとしたが、解らないことが多かったので備忘としてまとめることにした。

# 前提

- Alexa カスタムスキルが作成できる。（※作成済みであること。）
- Amazon Cognito を使える環境がある。
- Login with Amazonを使える環境がある。

# 目次

- 概要
- アカウントリンクとは
- Login with Amazon セキュリティプロファイルの作成
- Amazon Cognito ユーザープールの作成
- Alexa Skill アカウントリンクの設定
- Lambdaでユーザープール属性情報を取得するコードを書く
- アレクサアプリでスキルを有効にしアカウントリンクする
- スキルを実行しユーザープール属性情報が取得できることを確認する


# 概要

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/e07137a0-5a7e-082a-db79-0a8643c87552.png)

# アカウントリンクとは

アカウントリンクとは、ユーザーの `Amazon ID` を、ユーザーが持つ他のシステムのアカウントに接続する仕組み。

> たとえば、ウェブベースでユーザーがタクシーを手配できる「タクシー予約」サービスがあるとします。それを元にして「アレクサ、タクシー予約でタクシーを頼んで」とユーザーがリクエストするカスタムスキルを作成できます。 このリクエストを完了するには、スキルがタクシー予約のユーザーとしてタクシー予約サービスにアクセスし、プロファイル情報と支払い情報を取得する必要があります。つまり、Alexa搭載デバイスで使うAmazonアカウントと、ユーザーのタクシー予約アカウントとのリンクが必要ということです。

# Login with Amazon セキュリティプロファイルの作成

[Login with Amazonコンソール](https://developer.amazon.com/loginwithamazon/console/site/lwa/overview.html) を開き、`セキュリティプロファイルを新規作成` をクリックする。

必要事項を入力後、`保存` をクリックする。

|No.|項目|入力値|
|:--|:--|:--|
|1|セキュリティプロファイル名|プロファイル名を入力（任意の文字列）|
|2|セキュリティプロファイルの説明|説明文を入力（任意の文字列）|
|3|プライバシー規約同意書URL|規約同意書URLを入力（ここでは、https\://example.com/privacy.html とした。）|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/3f853d2a-6006-0191-b8d3-918ae2bf3129.png)


続いて `ウェブ設定` をする。しかし、今の時点では、Amazon Cognito を設定していないため設定ができない。そのため、この作業をスキップし Amazon Cognito 設定後にこの欄に入力する。
※既にAmazon Cognitoのユーザープールが準備できている場合は、下記の作業を進めて構わない。

セキュリティプロファイルの一覧から、目的のセキュリティプロファイルの管理（歯車アイコン）をクリックし、`ウェブ設定`、`編集`の順にクリックし、下記項目設定した後、`保存`をクリックする。

|No.|項目|設定値|
|:--|:--|:--|
|1|許可されたオリジン|https://**&lt;Cognitoのドメイン名&gt;**|
|2|許可された返信URL|https://**&lt;Cognitoのドメイン名&gt;**/oauth2/idpresponse|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/bbf68c17-f08e-73c6-13d0-344b6033df69.png)

# Amazon Cognito ユーザープールの作成

[AWS マネジメントコンソール](https://aws.amazon.com/jp/console/) にログインし、`すべてのサービス` から `Cognito` をクリックする。

`ユーザープールの管理` をクリックする。
※画面が切り替わる。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/923b00b2-56ba-3a6a-eed7-6501e5d95cb9.png)



`ユーザープールを作成する` をクリックする。
画面が切り替わるので、`プール名` を入力し、 `デフォルトを確認する` をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/1612361b-7734-5676-f25d-8bdd1dabbf57.png)

画面を最下部までスクロールさせ、 `プールの作成` をクリックする。
`ユーザープールは正常に作成されあした。`と表示されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/04758928-8d84-3507-7a17-3988a793f6bb.png)

#### アプリクライアントの作成をする。
左のメニューから `アプリクライアント` をクリックし、画面が切り替わった後に `アプリクライアントの作成` をクリックする。そして、`アプリクライアント名` 欄にアプリクライアント名を入力し  `アプリクライアントの作成` をクリックする。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/6a0abcfa-3b51-c914-4b3f-d437c8192a3d.png)



#### アプリクライアントの設定

左のメニューから `アプリクライアントの設定` をクリックし、必要事項を入力（選択）後、`変更の保存` をクリックする。
※ [ベンダーID](https://developer.amazon.com/alexa/console/ask/settings)の確認

|No.|項目|設定値|
|:--|:--|:--|
|1|コールバック URL|https\://alexa.amazon.co.jp/api/skill/link/**&lt;Vendor ID&gt;**, https\://layla.amazon.com/api/skill/link/**&lt;Vendor ID&gt;**, https\://pitangui.amazon.com/api/skill/link/**&lt;Vendor ID&gt;**|
|2|サインアウト URL|※空欄のまま|
|3|許可されている OAuth フロー|Authorization code grant|
|4|許可されている OAuth スコープ|openid|
|||aws.cognito.signin.user.admin|
|||profile|

> *__openid__*
> 　クライアントが読み取り可能なIDトークン内のすべてのユーザー属性を返します。
> 　openid スコープ がクライアントから要求されていない場合、IDトークンは返却されません。

> *__aws.cognito.signin.user.admin__*
> 　アクセストークンを必要とする Amazon Cognito User Pool APIオペレーションへのアクセスを許可する。

> *__profile__*
> 　クライアントが読み取り可能なすべてのユーザー属性へのアクセスを許可します。
> 　※ *__phone__* と *__email__* は *__profile スコープ__*に含まれているためチェックボックスをオンにする必要はありません。

※`コールバック URL` は、Alexaスキルのアカウントリンクの設定画面、 `Alexaのリダイレクト先のURL` からも取得できる。

[ユーザープールのアプリクライアントの設定](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/86967966-dfbe-45fa-c0b5-60a53fc19322.png)

#### ドメインのプレフィックス の設定

左のメニューから `ドメイン名` をクリックし、`ドメインのプレフィックス`に任意の文字列を入力。
`ドメインのプレフィックス` に文字を入力すると、`変更の保存` ボタンが表示されるので、クリックするが、まずは、`ドメインのプレフィックス` が `使用可能かチェック` した後、問題なければ `変更の保存` をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/bc038f64-564f-6305-4169-439808c3dbb8.png)


#### ユーザープールの IDプロバイダーとして Login with Amazon を紐づける

左のメニューから `IDプロバイダー` をクリックし、`Login with Amazon` のアイコンをクリックする。

|No.|項目|設定値|
|:--|:--|:--|
|1|Amazon アプリ ID|Login with AmazonのクライアントID|
|2|アプリシークレット|Login with Amazonのクライアントシークレット|
|3|承認スコープ|profile|

入力後、`Login with Amazon の有効化` をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/ff4ed02f-83d1-5a78-2664-38dfcaaa27c8.png)



左のメニューから `属性マッピング` をクリックし、キャプチャ（取得したい項目）をユーザープール属性にマッピングし `変更の保存` をクリックする。今回は、**user_id** を **Username** に、**email** を **Email** にマッピングした。
※これがLambda関数で取得できる項目となる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/6ee9771d-7b84-e570-a2a7-8250b48bbe60.png)



# Alexa Skill アカウントリンクの設定

※Alexaスキルが作成されていること

[Alexa Developer Console](https://developer.amazon.com/alexa/console/ask) でアカウントリンクを実装するスキルを選択する。
`ビルド` タブをクリックし、左のメニューから `アカウントリンク` をクリックして以下の設定情報を入力する。
※ [ベンダーID](https://developer.amazon.com/alexa/console/ask/settings)の確認


|No.|項目|設定値|
|:--|:--|:--|
|1|Web認証画面のURI|https://**&lt;Cognitoのドメイン名&gt;**/oauth2/authorize?redirect_url=https\://pitangui.amazon.com/api/skill/link/**&lt;Vendor ID&gt;**|
|2| アクセストークンのURI|https://**&lt;Cognitoのドメイン名&gt;**/oauth2/token|
|3|ユーザーのクライアントID|CognitoのアプリクライアントID|
|4|ユーザーのシークレット|Cognitoのアプリクライアントのシークレット|
|5|ユーザーの認可スキーム|HTTP Basic認証 （推奨）|
|6|スコープ|profile|
||スコープ|openid|
||スコープ|aws.cognito.signin.user.admin|
|7|ドメインリスト|**&lt;Cognitoのドメイン名&gt;** ※https:// は含めないこと|


※ `Alexaのリダイレクト先のURL` は　Amazon Cognitoの `コールバック URL`に入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/f5d080f3-36d9-5936-169c-b9f134a5e469.png)


# Lambdaでユーザープール属性情報を取得するコードを書く

※IAMロールに、Cognitoを操作できる権限が付いている必要がある。
ただし、`ASC CLI` で作成したスキルは、作成時に権限が割り当てられているようで、改めてIAMロールを追加する必要はないみたい。

`/oauth2/userInfo` にGETリクエストを送るとスキル内で `ユーザープール属性` が取得できる。
**access_token** には `handlerInput.requestEnvelope.session.user.accessToken` を設定する。

> *__リクエスト__*
> GET https://**&lt;Cognitoのドメイン名&gt;**/oauth2/userInfo
> Authorization: Bearer **&lt;access_token&gt;**

```javascript:/oauth2/userInfo
const Alexa = require('ask-sdk-core');
const rp    = require('request-promise');

var rp_options = {
    "uri": "https://takara.auth.ap-northeast-1.amazoncognito.com/oauth2/userInfo",
    "method": "GET",
    "headers": {
        "Authorization": "Bearer eyJraWQiOiI0YWI0Y29yaG4wcXBzWG0wSDh4UHJzRldiZW5vN3dEdmVGSEVvbDdNMlwvOD0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI2MmUzNDI1MS0wZjg1LTRiMDAtODU5NS03MTA1YzA4NTk0MmIiLCJjb2duaXRvOmdyb3VwcyI6WyJhcC1ub3J0aGVhc3QtMV9UTjg0TG1vMGRfTG9naW5XaXRoQW1hem9uIl0sInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4gb3BlbmlkIHByb2ZpbGUiLCJhdXRoX3RpbWUiOjE2MTAyOTAwMTQsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1ub3J0aGVhc3QtMS5hbWF6b25hd3MuY29tXC9hcC1ub3J0aGVhc3QtMV9UTjg0TG1vMGQiLCJleHAiOjE2MTAyOTM2MTQsImlhdCI6MTYxMDI5MDAxNCwidmVyc2lvbiI6MiwianRpIjoiYzc1ODc1ZWMtZjNhNC00YmEzLTgzYWEtNGY5Y2IzMTcxMzM2IiwiY2xpZW50X2lkIjoiM3NlMXV0bGgybzY4bGlnZ2NsZHYwdHZoYTQiLCJ1c2VybmFtZSI6ImxvZ2lud2l0aGFtYXpvbl9hbXpuMS5hY2NvdW50LmFlNzN5Z3ZqZWl5eGp5aDNndWQ1MmJiY2FqZ2EifQ.Q-bcpsc1cO72TjFAd7WnXI2_fSeGvQyu5fCWep7jMIurMW18CoVl7rN8d58huwNsY4o0vdgD0FBXDlxuncZhcdJjis0QtN2y2pK98YPqaxlSncpG66iIbqcj7ujgK7S8hWw8mbhRTdNmvKanV4E7hXrqi8cPTbJOpCj2Xjm3LANx5S_0kkA86QWaiDXrosXGoMULUTpalVPu9z-bHjZWP3YQWYIbSWJCxcjIpUd0ge_weRV_KSppqca3mCbN42vYWJbDDKxLPGNxA57kTn2UgnfLInRbVXmGTR7amAenf8dw-EHVyBHXPQZEOQ82rvBLSRm0tbRjp8tMtQNJpdcD1Q"
    }
};

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    async handle(handlerInput) {
        rp_options.headers.Authorization = 'Bearer ' + handlerInput.requestEnvelope.session.user.accessToken;
        await rp(rp_options)
            .then((res) => {
                console.log(`LaunchRequestHandler:res: ${JSON.stringify(res)}`);
            })
            .catch((err) => {
                 :
        });
        :
```




# アレクサアプリでスキルを有効にしアカウントリンクする

アレクサアプリで対象のスキルを有効にする。

<img width="30%" alt="アカウントリンク成功" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/14930148-131b-f41c-cb16-47e94534a3dd.png">　<img width="30%" alt="有効なスキルの一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/16e5c98c-c902-25f3-c178-5d69f1ebc6fc.png">　<img width="30%" alt="スキルの有効化" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/8fe36e15-1c98-a02f-c2e6-52fc9c5d7aaa.png">

`Login with Amazon`のログインページが表示されるので、EメールとAmazonのパスワードを入力し `ログイン` ボタンをクリックする。`正常にリンクされました。` と表示されれば成功。

<img width="30%" alt="Login with Amazonでログイン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/d849416d-ad19-1edf-a1ca-29ca1c57976e.png">　<img width="30%" alt="アカウントリンク成功" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/412d8980-2bff-434e-8425-906e4eb14f86.png">

# スキルを実行しユーザープール属性情報が取得できることを確認する

スキルを実行してみる。`/oauth2/userInfo` へのリクエストした結果、Cognitoの `属性マッピング` で設定した `ユーザープール属性`、**email** と **username** が取得できた。

```javascript:/oauth2/userInfoの取得結果
{
    "sub": "62e34251-0f85-4b00-8595-7105c085942b",
    "email_verified": "false",
    "email": "takara1121@yahoo.co.jp",
    "username": "loginwithamazon_amzn1.account.xx73ygvjeiyxjyh3gud52bbcxxxx"
}
```

# おわりに

解ってしまえばなんてことはないが...Amazon Cognito も Alexaスキルのアカウントリンクもはじめてだったので細かい設定がわかりにくかった印象だった。点在している情報を集めて、なんとかカタチにすることができた（発見できなかっただけかもしれないが・・・）。まとまった資料がなく全体感を理解するのが難しかったが、手を動かしたことで自分がやりたいことは理解できた。

# 参考

- [How to Set Up Alexa Account Linking with Amazon Cognito User Pools to Create a Personalized Customer Experience](https://developer.amazon.com/en-US/blogs/alexa/alexa-skills-kit/2019/11/how-to-set-up-alexa-account-linking-with-amazon-cognito-user-pools-to-create-a-personalized-customer-experience)
- [Amazon Cognito ユーザープール](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html)
- [Authorization code grantを設定する](https://developer.amazon.com/ja-JP/docs/alexa/account-linking/configure-authorization-code-grant.html)
- [USERINFO エンドポイント](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/userinfo-endpoint.html)
- [ユーザープールにソーシャルサインインを追加する (オプション)](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-configuring-federation-with-social-idp.html)
- [アカウントリンクとは](https://developer.amazon.com/ja-JP/docs/alexa/account-linking/understand-account-linking.html)
- [ユーザープールのアプリクライアントの設定](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html)