事象
Alexaアプリからアクセス権限を設定しても権限が保存されない。(アクセス権限:タイマー)
この事象は、Timer APIを使用したゆでたまごタイマースキルを公開(2020年6月12日)した直後に気づいてはいたが、有名企業の公開スキルでも同じ事象が確認できていたため、当時はスキル側の問題ではないと認識し問題視していなかった。
しかし、いっこうに事象が改善されないため、Amazon社に問合せしたところ、スキル側の問題であることが判明したので対策を実施した。
(動画)Alexaアプリでアクセス権限を設定したが、再度、権限を確認すると権限が外れている。
原因
Alexaアプリからスキルのアクセス権限を設定すると、権限を設定した瞬間にスキルが Alexa.Authorization.Grant
というレスポンスを受け取るらしいのだが、受け取った時に呼ばれるインターセプター内で使用している Alexa.getLocale
関数が Alexa.Authorization.Grant
を受け取る事を想定していなかったためエラーとなり、アクセス権限が保存される前にスキルが終了したと推測。根本原因は調査中。
(※Alexaアプリで権限を変更したときに、スキルがレスポンスを取得することを知らなかった。スマートホームスキルだったらわかるけど。)
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アプリからスキルのアクセス権限を設定すると、以下のレスポンスがスキルに送信され、スキルが実行されていた。
{
"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を受け取った後のスキルの動作としては、
- 上記のRESPONSE(
Alexa.Authorization.Grant
)をスキルが受け取る。 - インターセプター(
LocalisationRequestInterceptor
)が実行される。 -
Alexa.getLocale
でlocale
が取得できずエラーが発生する。 -
ErrorHandler
が実行されスキル終了。
RESPONSE( Alexa.Authorization.Grant
)をスキルが受け取ったときに、スキルはハンドラーを特定する前に、インターセプター( LocalisationRequestInterceptor
)を実行する。この時、インターセプター内で使用されている Alexa.getLocale
に渡している requestEnvelope(上記のRESPONSE)
には locale
が含まれていない。そのため Alexa.getLocale
で locale
が取得できずエラーが発生し、その影響でアクセス権限が保存される前にスキルが停止していたと推測している。
(※lng: Alexa.getLocale(handlerInput.requestEnvelope),
の Alexa.getLocale
で Cannot 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';
handlerInput.requestEnvelope.context.request.type === 'Alexa.Authorization.Grant';
2. i18next を使用しない
スキルの多言語化が必要ないのであれば、i18next
をやめてしまう。
要するに、インターセプターで requestEnvelope
を使用しないようにすることで、予想していないものを拾う可能性をなくす。
恒久対策
エラーとアクセス権限の保存の関係については、現在、Amazonさんが調査中。(2020年7月22日時点)
その結果がわかりしだい対策を考える。
参考
- アクセス権限を設定してAlexaへのユーザー認証を実現する(※Amazon公式サイト)
- Alexa.Authorizationインターフェース(※Amazon公式サイト)
- AlexaタイマーAPIリファレンス(※Amazon公式サイト)
- ゆでたまごタイマー