LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

Alexaアプリで設定したアクセス権限が保存されない問題(アクセス権限:タイマー)

事象

Alexaアプリからアクセス権限を設定しても権限が保存されない。(アクセス権限:タイマー)

この事象は、Timer APIを使用したゆでたまごタイマースキルを公開(2020年6月12日)した直後に気づいてはいたが、有名企業の公開スキルでも同じ事象が確認できていたため、当時はスキル側の問題ではないと認識し問題視していなかった。

しかし、いっこうに事象が改善されないため、Amazon社に問合せしたところ、スキル側の問題であることが判明したので対策を実施した。

デモ
(動画)Alexaアプリでアクセス権限を設定したが、再度、権限を確認すると権限が外れている。

原因

Alexaアプリからスキルのアクセス権限を設定すると、権限を設定した瞬間にスキルが Alexa.Authorization.Grant というレスポンスを受け取るらしいのだが、受け取った時に呼ばれるインターセプター内で使用している Alexa.getLocale 関数が Alexa.Authorization.Grant を受け取る事を想定していなかったためエラーとなり、アクセス権限が保存される前にスキルが終了したと推測。根本原因は調査中。
(※Alexaアプリで権限を変更したときに、スキルがレスポンスを取得することを知らなかった。スマートホームスキルだったらわかるけど。)

CloudWatchより
2020-07-21T08:49:54.080Z    8299eb2c-xxxx-xxxx-xxxx-718f4315cc7f    INFO    ~~~~ Error handled: TypeError: Cannot read property 'locale' of undefined
    at Object.getLocale (/var/task/node_modules/ask-sdk-core/dist/util/RequestEnvelopeUtils.js:26:36)
    at Object.process (/var/task/index.js:441:24)
    at GenericRequestDispatcher.<anonymous> (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:83:65)
    at step (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:45:23)
    at Object.next (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:26:53)
    at /var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:20:71
    at new Promise (<anonymous>)
    at __awaiter (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:16:12)
    at GenericRequestDispatcher.dispatch (/var/task/node_modules/ask-sdk-runtime/dist/dispatcher/GenericRequestDispatcher.js:71:16)
    at CustomSkill.<anonymous> (/var/task/node_modules/ask-sdk-core/dist/skill/CustomSkill.js:103:69)

スキルが受け取っていたRESPONSE

Alexaアプリからスキルのアクセス権限を設定すると、以下のレスポンスがスキルに送信され、スキルが実行されていた。

RESPONSE
{
    "version": "1.0",
    "context": {
        "System": {
            "application": {
                "applicationId": "amzn1.ask.skill.a50371d8-xxxx-xxxx-xxxx-87397c58045d"
            },
            "user": {
                "userId": "amzn1.ask.account.xxxxxxxxxxxxxxxxxxxxxxxxxxx"
            },
            "apiEndpoint": "api.fe.amazonalexa.com"
        },
        "request": {
            "type": "Alexa.Authorization.Grant",
            "requestId": "ffdb65ad-xxxx-xxxx-xxxx-463df2c366eb",
            "timestamp": "2020-07-21T09:28:50Z",
            "body": {
                "grant": {
                    "type": "OAuth2.AuthorizationCode",
                    "code": "SRJCmcfWxxxxxxxxxxxx"
                }
            }
        }
    }
}

なぜ権限が保存されない?(推測)

少し前の ASK CLI v2では、 ask new すると、以下のような i18next を使用した、LocalisationRequestInterceptorがデフォルトで生成されていた。
(※現在は、テンプレートが変更されたようで、ask new してもこのようなコードは生成されない。)

インターセプター(抜粋)
// This request interceptor will bind a translation function 't' to the handlerInput
// Additionally it will handle picking a random value if instead of a string it receives an array
const LocalisationRequestInterceptor = {
    process(handlerInput) {
        const localisationClient = i18n.init({
            lng: Alexa.getLocale(handlerInput.requestEnvelope),
            resources: languageStrings,
            returnObjects: true
        });

RESPONSEを受け取った後のスキルの動作としては、

  1. 上記のRESPONSE( Alexa.Authorization.Grant )をスキルが受け取る。
  2. インターセプター( LocalisationRequestInterceptor )が実行される。
  3. Alexa.getLocalelocale が取得できずエラーが発生する。
  4. ErrorHandler が実行されスキル終了。

RESPONSE( Alexa.Authorization.Grant )をスキルが受け取ったときに、スキルはハンドラーを特定する前に、インターセプター( LocalisationRequestInterceptor )を実行する。この時、インターセプター内で使用されている Alexa.getLocale に渡している requestEnvelope(上記のRESPONSE) には locale が含まれていない。そのため Alexa.getLocalelocale が取得できずエラーが発生し、その影響でアクセス権限が保存される前にスキルが停止していたと推測している。

(※lng: Alexa.getLocale(handlerInput.requestEnvelope),Alexa.getLocaleCannot read property 'locale' of undefined が発生する。)

応急対策

1. try - catch で回避

インターセプター内でエラーが発生しないようにする。try - catch で回避した。これでAlexaアプリから設定いたアクセス権限の情報が保存されるようになった。

インターセプター(抜粋)
// This request interceptor will bind a translation function 't' to the handlerInput
// Additionally it will handle picking a random value if instead of a string it receives an array
const LocalisationRequestInterceptor = {
    process(handlerInput) {
        try {
            const localisationClient = i18n.init({
                lng: Alexa.getLocale(handlerInput.requestEnvelope),
                resources: languageStrings,
                returnObjects: true
            });

ただし、インターセプターは通過するようになったものの、ハンドラー( LaunchRequestHandler )処理では、requestEnvelope(上記のRESPONSE) を元にリクエストタイプを取得するのだが、request.type の階層が異なるため Alexa.getRequestType では、タイプを取得できず TypeError: Cannot read property 'type' of undefined エラーを引き起こす。しかし、ハンドラー内のエラーであれば、理由は不明だがアクセス権限は保存される。今のところはこれでアクセス権限の保存問題は回避できているので、いったん調査はストップ。

インテントタイプ
        handlerInput.requestEnvelope.request.type === 'LaunchRequest';
Alexa.Authorization.Grant
        handlerInput.requestEnvelope.context.request.type === 'Alexa.Authorization.Grant';

2. i18next を使用しない

スキルの多言語化が必要ないのであれば、i18next をやめてしまう。
要するに、インターセプターで requestEnvelope を使用しないようにすることで、予想していないものを拾う可能性をなくす。

恒久対策

エラーとアクセス権限の保存の関係については、現在、Amazonさんが調査中。(2020年7月22日時点)
その結果がわかりしだい対策を考える。

参考

以上

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0