アプリ間アカウントリンク(開発者のアプリから開始する場合)
アプリ間アカウントリンク(開発者のアプリから開始する場合)
注: このアカウントリンクフローの機能サポートの状況は次のとおりです。 • アプリからAlexaアプリを起動 - iOSとAndroid • Login with Amazon - ウェブ、およびiOSとAndroidのフォールバック
このアカウントリンクフローを使用すると、ユーザーはアプリやウェブサイトから、AlexaユーザーIDと別のサービスでのIDをリンクできるようになります。アプリからアカウントリンクフローを開始すると、ユーザーは以下のことができます。
アプリからAlexaスキルを検索できます。
アプリからスキルを有効化し、アカウントをリンクできます
モバイルデバイスでアプリとAlexaアプリの両方にログインしている場合、どちらのアプリにもアカウント認証情報を入力せずにアカウントをリンクできます。
モバイルデバイスにAlexaアプリをインストールしていない場合、アプリからLogin with Amazon(LWA) を使ってアカウントをリンクできます。
次の方法でも、アカウントリンクフローを実装することができます。
アプリやウェブサイトをお持ちの場合は、Alexaアプリのみのフローに加えて、いずれかのアプリ間アカウントリンクフローの実装をお勧めします。
これ以降に出現するアプリ間アカウントリンクという用語は、開発者のアプリから開始されたアプリ間アカウントリンクを指します。
トラブルシューティングに関する情報については、アプリ間アカウントリンク(開発者のアプリから開始する場合)のトラブルシューティング を参照してください。
用語
このトピックでは、以下の用語を使用します。
サービス - 開発者が提供するサービスです。たとえば、ユーザーがタクシーを手配できる、ウェブベースの「タクシー予約」サービスなどがあります。
アプリ - ユーザーがサービスの操作に使用するアプリです。先ほどの例であれば、「タクシー予約」アプリなどです。ここでは、読者がアプリの開発者だと想定します。
スキル - ユーザーがAlexaでサービスを操作するために使用するAlexaスキルです。ここでは、読者がスキルの開発者だと想定します。
Alexaアプリ - ユーザーがモバイルデバイスにダウンロードして使用できるAmazon Alexaアプリ です。
Login with Amazon(LWA) - ユーザーがログインして、自身のユーザープロファイルデータへのアクセス権限を付与できる認証システムです。アプリ間アカウントリンクの場合、LWAは、ユーザーがモバイルデバイスにAlexaアプリをインストールしていない場合に備えて実装する予備システム(フォールバック)です。LWA全般については、Login with Amazonに関するドキュメント を参照してください。
OAuth 2.0 - ユーザーが以前に設定したアカウントから、ユーザーの権限を使ってAlexaが情報にアクセスできるようにする認証の標準です。OAuth 2.0標準については、OAuth 2.0 を参照してください。
アプリリンク - ユーザーがアプリを起動するためにクリックするAndroidのディープリンクです。アプリリンクの詳細については、Androidのドキュメント を参照してください。
ユニバーサルリンク - ユーザーがアプリを起動するためにクリックするiOSのディープリンクです。ユニバーサルリンクの詳細については、iOSのドキュメント を参照してください。
ユーザーの操作
ユーザーは以下のワークフローに従って、アプリ間アカウントリンクを有効にします。
スキルを有効にし、アカウントをAlexaにリンクするためのオプションがアプリに表示されます。ユーザーがアカウントのリンクを選択します。モバイルデバイスにAlexaアプリがインストールされているかどうかによって、以下のいずれかのフローが適用されます。
(Alexaアプリのフロー)モバイルデバイスにAlexaアプリがインストールされている場合は、Alexaアプリが起動し、ユーザーに対してアカウントリンクリクエストへの同意を求めます。 ユーザーがリクエストに同意すると、アプリにリダイレクトされます。
(LWAのフロー)モバイルデバイスにAlexaアプリがインストールされていない場合、アプリ内のブラウザウィンドウにLWAが表示されます。ユーザーはAmazonの認証情報を入力するか、Amazonアカウントを作成します。 次にユーザーは、スキルにアカウントをリンクする権限を付与するように求められます。ユーザーがリクエストに同意すると、アプリにリダイレクトされます。
アカウントをリンクした後、ユーザーがスキルを使用するワークフローは、Alexaアプリのみ のアカウントリンクと同じです。いずれの場合も、スキルを無効にすると、アカウントリンクが解除されます。
以下は、アプリ間アカウントリンクのスクリーンショットの例です。前述のように、フローはユーザーがAlexaアプリをインストールしているかどうかによって決まります。
Alexaアプリがインストールされている場合
以下はAlexaアプリのフローの例です。
Alexaアプリがインストールされていない場合
以下はLWAのフローの例です。
実装するフローの選択
AlexaアプリのフローはiOSとAndroidで実装できます。したがって、実装するフローは、開発しているアプリ(iOSアプリ、Androidアプリ、ウェブサイト)によって異なります。
iOSアプリとAndroidアプリ - Alexaアプリのフローをメインのフローとして実装し、Alexaアプリがインストールされていない場合のフォールバックとしてLWAのフローを実装します。
ウェブサイト - LWAのフローを実装します。
しくみ
アプリ間アカウントリンクはOAuth 2.0 に基づいて機能します。以下の図では、ユーザーがモバイルデバイスのアプリからアカウントリンクを開始した場合の処理の流れを示します(クリックで拡大します)。
処理の流れは以下のとおりです。各URLについては、URLとエンドポイント を参照してください。
ユーザーがモバイルデバイスにアプリをインストールして、ログインします。
アプリ画面に、スキルを有効にし、ユーザーのアカウントをAlexaにリンクするオプションが、そのメリット(「Alexaから音声で『タクシー予約』の配車を手配できます」など)とともに表示されます。 ユーザーがリンクのリクエストに同意します。
次のステップは、ユーザーのデバイスにAlexaアプリがインストールされているかどうかによって、以下の2つに分かれます。
Alexaアプリがインストールされている場合:
アプリが、AlexaアプリのURLと認可リクエストパラメーター (後ほど 説明します)を使ってAlexaアプリを起動します。
Alexaアプリが起動し、ユーザーに対してAlexaをサービスにリンクするかをたずねる画面が表示されます。
ユーザーがリンクのリクエストに同意します。
Alexaアプリが、ユーザーのリダイレクト先のURLを使ってユーザーをアプリに戻します。このリダイレクトでは、ユーザーのAmazon認可コードも送信されます。
Alexaアプリがインストールされていない場合:
アプリがLWAフォールバックURLと認可リクエストパラメーター (後ほど 説明します)を使って、(ネイティブのブラウザアプリではなく)アプリ内ブラウザタブでLWAを起動します。
LWAが起動し、ユーザーにAmazonアカウントへのログインを求めます。
LWAに、ユーザーに対してAlexaをサービスにリンクするかをたずねる画面が表示されます。ユーザーがリンクのリクエストに同意します。
LWAが、ユーザーのリダイレクト先のURLを使ってユーザーをアプリに戻します。このリダイレクトでは、ユーザーのAmazon認可コードも送信されます。
バックエンドサーバーがLWA認可サービスのURLを呼び出し、前のステップで取得したAmazon認可コード をAmazonアクセストークンと交換します。
バックエンドサーバーが認可サーバーを呼び出し、ユーザーの認可コード(サービスでユーザーが保有するアカウントの認可コード)を取得します。
バックエンドサーバーが、ユーザーのAmazonアクセストークンおよびサービスでのユーザーの認可コードを使ってAlexaスキル有効化API を呼び出し、スキルの有効化とアカウントのリンク を行います。
AlexaがアプリのアクセストークンのURLを呼び出し、サービスに対するユーザーの認可コードをサービスでのアクセストークンと交換します。これでアカウントリンクの処理は完了です。
URLとエンドポイント
このセクションでは、アプリ間アカウントリンク関連のURLとエンドポイントについて説明します。パラメーターの説明がその後に続きます。
AlexaアプリのURL
リンクリクエストへの同意を得るために、ユーザーをAlexaアプリにリダイレクトするユニバーサルリンク(iOS)またはアプリリンク(Android)です。パラメーターの詳細については、AlexaアプリのURLとLWAフォールバックURLのパラメーター を参照してください。ユニバーサルリンク全般については、AppleによるAllowing Apps and Websites to Link to Your Content のドキュメントを参照してください。
AlexaアプリのURLとLWAフォールバックURLは、バックエンドに保存してGETリクエストで取得できます。またこれらのURLをローカルに保存しておけば、余分のネットワーク呼び出しを回避できます。ただし、今後、スキルのセキュリティプロファイルを変更する際には、新しいビルドが必要になる可能性があります。
形式: https://alexa.amazon.com/spa/skill-account-linking-consent?fragment=skill-account-linking-consent&client_id={ClientId}&scope=alexa::skills:account_linking&skill_stage={skillStage}&response_type=code&redirect_uri={yourRedirectUrl}&state={yourState}
例: https://alexa.amazon.com/spa/skill-account-linking-consent?fragment=skill-account-linking-consent&client_id=amzn1.application-oa-client-1&scope=alexa::skills:account_linking&skill_stage=development&response_type=code&redirect_uri=https://yourAppRedirectUri&state=aGVsbG8
LWAフォールバックURL
Amazon認証情報の入力を求めるため、ユーザーをLWAに送信するリンクです。iOS、Android、ウェブサイトのいずれにも使用できます。ユーザーのデバイスにAlexaアプリがインストールされていない場合に、このリンクを使用します。パラメーターの詳細については、AlexaアプリのURLとLWAフォールバックURLのパラメーター を参照してください。
URLの保存については、前述のAlexaアプリのURLに関する説明を参照してください。
形式:https://www.amazon.com/ap/oa?client_id={ClientId}&scope=alexa::skills:account_linking&response_type=code&redirect_uri={yourRedirectUrl}&state={yourState}
例: https://www.amazon.com/ap/oa?client_id=amzn1.application-oa-client-1&scope=alexa::skills:account_linking&response_type=code&redirect_uri=https://yourAppRedirectUri&state=aGVsbG8
アプリのリダイレクト先のURL
ユーザーがAlexaアプリまたはLWAでリンクリクエストに同意した後、Alexaアプリ(Alexaアプリがインストールされていない場合は、LWA)がユーザーをアプリに戻すためのユニバーサルリンク またはアプリリンク です。アプリは、このリダイレクトで返されるパラメーターから、ユーザーのAmazon認可コードを取得します。このコードの有効期限は5分間です。
Alexa開発者コンソール、ASK CLI、またはSMAPIを使用して、このようなURLを1つ以上指定します。必須の構文については、URI specification を参照してください。
認証画面のURL
認可サーバーのURLです。認可サーバーはユーザーの認証情報を受け付け、ユーザーを認証し、認可コードを生成する必要があります。その後、Alexaアプリは、生成された認可コードを認可サーバーに渡し、サービスでユーザーを一意に識別するアクセストークンを取得します。
このリンクは、通常の(アプリ間ではない)アカウントリンクにのみ使用されます。開発者コンソール、ASK CLI、またはSMAPIを使用して指定します。
アクセストークンのURL
Alexaが、ユーザーに対するサービスの認可コードをアクセストークンと交換するトークンサーバーです。これで、アカウントリンクが完了します。
開発者コンソール、ASK CLI、またはSMAPIを使用して指定します。
LWA認可サービス
このリンクは、バックエンドサーバーが、ユーザーのAmazon認可コードをAmazonアクセストークンと交換するために使用します。アプリはAlexaサービスを呼び出したうえで、このアクセストークンを使ってユーザーにスキルを有効化し、アカウントをリンクします。
Host: https://api.amazon.com
、POSTリクエストの送信先は/auth/o2/token
Alexaスキル有効化APIのエンドポイント
このエンドポイントは、バックエンドサーバーが、ユーザーに対してスキルを有効にするために呼び出します。Alexaスキル有効化APIの詳細については、Alexaスキル有効化API を参照してください。
形式: https://api.amazonalexa.com/v1/users/~current/skills/{yourSkillId}/enablement
ユーザーの所在地に応じて、ベースURLはhttps://api.amazonalexa.com
、https://api.eu.amazonalexa.com
、https://api.fe.amazonalexa.com
のいずれかです。
AlexaアプリのURLとLWAフォールバックURLのパラメーター
Client ID
開発者コンソールでアプリ間アカウントリンクを有効にするときに、Alexa開発者コンソールで提供されるAlexaクライアントIDです。
Client secret
開発者コンソールでアプリ間アカウントリンクを有効にするときに、開発者コンソールで提供されるAlexaクライアントシークレットです。
Redirect URL
開発者コンソールでアプリ間アカウントリンクを有効にするときに指定するユニバーサルリンク またはアプリリンク です。ユーザーがアカウントリンクリクエストに同意すると、AlexaアプリとLWAがユーザーをこのURLに戻します。
Scope
アプリがスキルを有効にする権限を得るには、このフィールドをalexa::skills:account_linking
に設定する必要があります。
Skill ID
一意のスキルIDです。この情報は開発者コンソールで確認できます。
Stage
スキルのステージです。スキルが公開されているかどうかを表します。スキルを公開するまでは、stage
をdevelopment
に設定します。スキルの公開時にlive
に変更します。
response type
応答タイプはcode
に設定する必要があります。アプリ間アカウントリンクは現在、Authorization code grantにのみ対応しているためです。
state
現在のリクエストと応答の間のステートを管理するために使用するopaqueな値です。AlexaアプリのURLには、ステート値が必須です。Alexaは、ユーザーのリダイレクト先のURLを使ってユーザーをアプリに戻すとき、このステートをそのまま応答に入れて返送します。リクエスト強要(CSRF) を防ぐには、ステートを使用してリクエストを検証する必要があります。
生成したステートに、&
、=
、‘
、/
、\
、<
、>
、“
、#
、|
が含まれていないことを確認します。base64 URIを使用すると、確実です。実装例については、ステップ4: ユーザーのAmazon認可コードを取得する のステートヘルパーコード例を参照してください。
セキュアな乱数ジェネレーターを使用してステートを生成し、セッションに保存して検証することができます。またはバックエンドサーバーでステートを生成し、キーを使用して暗号化と復号化を行うことも可能です。
主な手順
このセクションでは、主な手順を説明し、以下のライブラリと言語を使用したiOSアプリ(10.0以降)およびAndroidアプリのコード例を紹介します。
以下の図では、主な手順を示します。図に続いて手順を説明し、コード例を示します。
クリックすると、各ステップにジャンプします。 1 2 3 4 5 6 7 8
ステップ1: アプリのアカウントリンクオプションを表示する
アプリには、ユーザーがスキル有効化とアカウントリンクを開始できる画面が必要です。このページには、ユーザーが自分のアカウントとAlexaをリンクして実現できる機能を表示します。たとえば、音声でタクシーの手配ができるなどです。
実装方法
例: iOSでアカウントリンクボタンを追加する
例: Androidでアカウントリンクボタンを追加する
ステップ2: アプリのユニバーサルリンク(iOS)またはアプリリンク(Android)を有効にする
Alexaアプリがアプリにユーザーをリダイレクトできるように、ユニバーサルリンク(iOSアプリの場合)またはアプリリンク(Androidアプリの場合)を有効にする必要があります。
実装方法
この手順については、以下の情報を参照してください。
ユニバーサルリンクとアプリリンクは、URI構文 の仕様に準拠している必要があります。追加したドメインとパスを覚えておいてください。開発者コンソールで、スキルの詳細としてリダイレクト先のURLを指定するときに必要になります。
注: Android 11(APIレベル30)以上のAndroid版のアプリでアプリリンクをサポートするには、パッケージ情報で
<queries>
要素を宣言する必要があります。
<queries>
要素の詳細については、公式の
Androidドキュメント を参照してください。
例: iOSアプリのユニバーサルリンクを有効にする
例: Androidアプリのアプリリンクを有効にする
ステップ3: スキルでアプリ間アカウントリンクを使用するためのコンフィギュレーションを行う
開発者コンソール 、Alexa Skills Kitコマンドラインインターフェース(ASK CLI) 、またはAlexaスキル管理API(SMAPI) を使用してスキルの設定を行います。このコンフィギュレーションでは、アプリのリダイレクト先のURL、アクセストークンのURLなどを指定します。
実装方法
例: 開発者コンソールを使ってスキルのコンフィギュレーションを行う
例: CLIを使ってスキルのコンフィギュレーションを行う
例: SMAPIを使ってスキルのコンフィギュレーションを行う
ステップ4: ユーザーのAmazon認可コードを取得する
アプリは、ユーザーのAmazon認可コードを取得する必要があります。認可コードは後でAmazonアクセストークンと交換できます。Amazonアクセストークンがあれば、アプリがスキルを有効にし、アカウントリンクを完了できます。Amazon認可コードの有効期限は5分間です。
実装方法
AlexaアプリのURLを開く(Alexaアプリのフローの場合)か、またはHTTP GETリクエストをLWA認可サーバーに送信(LWAのフローの場合)して、ユーザーのAmazon認可コードを取得します。iOSとAndroidでは、Alexaアプリのフローを実装し、LWAの使用を予備にとどめることを強くお勧めします。
ユーザーのAmazon認可コードを取得するには、認可リクエスト をアセンブルした後、URLとエンドポイント の説明にあるパラメーターを使用して、AlexaアプリのURL(Alexaアプリがインストールされていない場合は、LWAフォールバックURL)を開きます。これらのURLは、可能な限りバックエンドサーバーでアセンブルし、アセンブルされたURLをアプリに渡すことをお勧めします。そうすれば、アプリを再ビルドせずにパラメーター(ステージなど)をすぐに変更できます。
ユーザーがリンクリクエストを承認すると、Alexaアプリはアプリのリダイレクト先のURL(AlexaアプリのURLまたはLWAフォールバックURLのパラメーター)にユーザーをリダイレクトします。リダイレクトには、次に説明する成功またはエラーのパラメーターが含まれます。
ユーザーのデバイスが、指定したリダイレクト先のURLを使用してアプリを起動できない場合、ユーザーはデフォルトのブラウザで同じリダイレクト先のURLにリダイレクトされます。これは、たとえば次のような場合に発生します。
アプリでユニバーサルリンクまたはアプリリンクが有効になっていない。
指定したリダイレクト先のURLのアプリで、ユニバーサルリンクまたはアプリリンクが有効になっていない。
ユーザーのデバイス上のアプリのバージョンが古く、ユニバーサルリンクまたはアプリリンクをサポートしていない。
そのような場合は、ウェブページを(ユニバーサルリンクまたはアプリリンクと同じURLに)用意して認可コードを受け取る必要があります。ウェブページに移動したユーザーは、サービスから認可コードを取得するためにログインが必要になる場合があります。
ユーザーに表示するエラーとメッセージの例については、Amazon認可コード取得時のエラー を参照してください。
AlexaアプリのURLへのHTTP GETリクエストに対する成功応答
成功した場合は、以下のように、ユーザーのAmazon認可コードを含むリダイレクト先URLとその他のパラメーターが入った応答が返されます。この応答の処理方法については、Supporting Universal Links in Your App にあるAppleのガイドラインおよびAndroid アプリリンクの処理 にあるAndroidのガイドラインを参照してください。
// Alexaアプリからの成功応答の例
https://yourRedirectURL?code={Amazon Authorization code}&state={your state}
// LWAからの成功応答の例
https://yourRedirectURL?code={Amazon Authorization code}&scope={permission scope}&state={your state}
パラメーターの説明:
code
: AlexaアプリまたはLWAが返すAmazon認可コードです。
state
: 認可リクエストで渡したステートです。このステートを検証してリクエスト強要を防止する必要があります。
scope
: 権限のスコープです。LWAは、alexa::skills:account_linking scope
を返します。Alexaアプリはscopeパラメーターを返しません。
OAuth2.0認可応答の詳細については、Authorization Response を参照してください。
AlexaアプリのURLへのHTTP GETリクエストに対するエラー応答
エラーの場合は、以下のように、リダイレクト先のURL、エラーの説明、その他のパラメーターが入った応答が返されます。アプリに応じた適切な形でエラーメッセージを表示してください(エラーページ、UIAlertController など)。表示するエラーメッセージについては、Amazon認可コード取得時のエラー を参照してください。
// エラー応答の例
HTTP 200
https://yourRedirectURL?error_description={エラーの説明}&state={開発者のステート}&error={OAuthエラーコード}
パラメーターの説明:
error
: ASCIIエラーコードが1つだけ返されます。値は以下のいずれかです。 invalid_request
、unauthorized_client
、access_denied
、unsupported_response_type
、invalid_scope
、server_error
、temporarily_unavailable
。
error_description
: エラーの説明です。
OAuth2.0のエラー応答の詳細については、Error Response を参照してください。
iOSの例
例: URLを取得する(iOS)
この例では、アプリがAlexaアプリのURLとLWAフォールバックURLの両方を取得しています。
import Alamofire
import SwiftyJSON
class AlamofireHelper {
// URLのリクエスト元であるバックエンドのベースURL
static let BASE_URL = "https://exampleBackEndURL"
/*! AlexaアプリのユニバーサルリンクとLWAフォールバックURLを取得します。
@param userAccessToken バックエンドサーバー向けです。
@param state クライアントとサーバーの間で維持されます。呼び出し側が生成する必要があります。
*/
static func getAlexaAppUrl(userAccessToken:String, state: String) -> DataRequest {
let alexaAppUrl = "/alexaurl"
let inputstate = "?state=" + state
var urlRequest = URLRequest(url: URL(string: BASE_URL + alexaAppUrl + inputstate)!)
urlRequest.addValue("Bearer " + userAccessToken, forHTTPHeaderField: "Authorization")
return Alamofire.request(urlRequest)
}
private init() {}
}
// バックエンドを呼び出して認可リクエストURLを取得します(Alexaアプリ/LWA)。
AlamofireHelper.getAlexaAppUrl(userAccessToken: savedSession.accessToken, state: StateHelper.generateState()).responseJSON {
response in
guard let status = response.response?.statusCode, 200..<300 ~= status else {
self.createAlert(title: "Alexa URLの取得に失敗しました", message: "Alexa URLの取得に失敗しました")
return
}
guard let responseValue = response.result.value else {
self.createAlert(title: "無効な応答です", message: "無効な応答です")
return
}
let reponseInJSON = JSON(responseValue)
guard let companionApp = reponseInJSON["companionAppURL"].string,let lwaFallback = reponseInJSON["LWAFallBackURL"].string else {
self.createAlert(title: "エラー応答", message: "エラー応答")
return
}
guard let companionAppURL = URL(string: companionApp), let lwaFallbackURL = URL(string: lwaFallback) else {
self.createAlert(title: "URLの形式が誤っています", message: "URLの形式が誤っています")
return
}
// openUniversalLinksコードスニペットを使用した別の例を示します。
self.openUniversalLinks(companionAppURL: companionAppURL, lwaFallbackURL: lwaFallbackURL)
}
例: URLを開く(iOS)
この例では、アプリがAlexaアプリのURLを開き、Alexaアプリがインストールされていない場合は、LWAフォールバックURLを開きます。
以下に注意してください。
import SafariServices
// universalLinksOnlyオプションを使ってAlexa URLを開きます。
// ユニバーサルリンクが開かない場合は、LWAフォールバックを開きます。
private func openUniversalLinks(companionAppURL: URL, lwaFallbackURL: URL) {
UIApplication.shared.open(companionAppURL, options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly:true]) {
companionAppLaunched in
if !companionAppLaunched {
if #available(iOS 12.0, *) {
WebSession.initWebAuthenticationSession(authURL: lwaFallbackURL)
} else if #available(iOS 11.0, *) {
WebSession.initAuthenticationSession(authURL: lwaFallbackURL)
} else {
let safariViewController = SFSafariViewController(url: lwaFallbackURL)
self.present(safariViewController, animated: true, completion: nil)}
}
}
}
// ウェブセッション
class WebSession {
@available(iOS, introduced: 11.0, deprecated: 12.0)
private static var authenticationSession: SFAuthenticationSession?
@available(iOS 12.0, *)
private static var webAuthenticationSession : ASWebAuthenticationSession?
private static let CALLBACK_URL_SCHEME = "https://yourAppsUniversalLinkURL"
@available(iOS, introduced: 11.0, deprecated: 12.0)
static func initAuthenticationSession(authURL:URL) {
if let session = authenticationSession {
session.cancel()
}
self.authenticationSession = SFAuthenticationSession.init(url: authURL, callbackURLScheme: CALLBACK_URL_SCHEME, completionHandler:{
(callBack:URL?, error:Error?) in
//コールバックとエラー処理
})
self.authenticationSession?.start()
}
@available(iOS 12.0, *)
static func initWebAuthenticationSession(authURL:URL) {
if let session = webAuthenticationSession {
session.cancel()
}
self.webAuthenticationSession = ASWebAuthenticationSession.init(url: authURL, callbackURLScheme: CALLBACK_URL_SCHEME, completionHandler:{
(callBack:URL?, error:Error?) in
// コールバックとエラー処理
})
self.webAuthenticationSession?.start()
}
private init(){}
}
例: LWAのURLを開く(iOS)
ウェブサイトの場合や、iOSデバイスにAlexaアプリがインストールされていない場合のフォールバックとしてLWAのURLを開きます。この例では、アプリがView ControllerでLWAを開いています。
import SafariServices
// Safari View ControllerでLogin with Amazonを開きます。
private func openSafariView(lwaFallbackURL: URL) {
let safariViewController = SFSafariViewController(url: lwaFallbackURL)
self.present(safariViewController, animated: true, completion: nil)}
}
例: ユニバーサルリンクの応答を処理する(iOS)
この例では、Alexaアプリからアプリのユニバーサルリンクへの呼び出しについて、アプリがそのパラメーターを検証して抽出します。
// AppDelegateのユニバーサルリンクハンドラー
// ユニバーサルリンクを処理するメソッドを追加します。
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL else {
return false
}
let handlers : [UniversalLinkHandler] = [AuthResponseHandler(), ErrorResponseHandler()]
var validatedResponse : ValidatedResponse?
for handler in handlers {
if handler.canHandle(incomingURL: incomingURL) {
validatedResponse = handler.getValidatedResponse()
let initialViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "linkingStatusScreen") as! LinkingStatusViewController
initialViewController.response = validatedResponse
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
}
return false
}
// ユニバーサルリンクハンドラーのプロトコル
protocol UniversalLinkHandler {
// 受け取ったURLスキームを検証します。
func canHandle(incomingURL:URL)-> Bool
//検証応答を取得します。
func getValidatedResponse() -> ValidatedResponse?
}
extension UniversalLinkHandler {
func validateState(state:String)->Bool {
return StateHelper.validateState(state: state)
}
}
// Amazon認可コードを受信します。
class AuthResponseHandler : UniversalLinkHandler {
private let AUTH_RESPONSE_PARAMETERS: Set = ["code", "state", "scope"]
private var validatedResponse: ValidatedResponse?
func canHandle(incomingURL: URL) -> Bool {
guard let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let queryParameters = components.queryItems else {
return false
}
var validatedParameters : [String: String] = [:]
for queryParameter in queryParameters {
//重複したURLのパラメーター
if validatedParameters.keys.contains(queryParameter.name) {
return false
}
//認識できないURLのパラメーター
if !AUTH_RESPONSE_PARAMETERS.contains(queryParameter.name) {
return false
}
validatedParameters[queryParameter.name] = queryParameter.value
}
guard let code = validatedParameters["code"], let state = validatedParameters["state"] else {
return false
}
//ステートを検証します。
if !validateState(state: state) {
self.validatedResponse = ErrorResponse(url: incomingURL,error: "無効なステート", errorDescription: "リクエストのステートが無効です")
return true
}
self.validatedResponse = AuthResponse(url: incomingURL,code: code, state: state, scope: validatedParameters["scope"])
return true
}
func getValidatedResponse() -> ValidatedResponse? {
return validatedResponse
}
}
// エラー認可応答ハンドラー
class ErrorResponseHandler : UniversalLinkHandler {
private let ERROR_RESPONSE_PARAMETERS: Set = ["error", "error_description", "state"]
private var validatedResponse: ValidatedResponse?
func canHandle(incomingURL: URL) -> Bool {
guard let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let queryParameters = components.queryItems else {
return false
}
// すべてのクエリーパラメーターを検証します。
var validatedParameters : [String: String] = [:]
for queryParameter in queryParameters {
// 重複したURLのパラメーター
if validatedParameters.keys.contains(queryParameter.name) {
return false
}
// 認識できないURLのパラメーター
if !ERROR_RESPONSE_PARAMETERS.contains(queryParameter.name) {
return false
}
validatedParameters[queryParameter.name] = queryParameter.value
}
guard let error = validatedParameters["error"], let errorDescription = validatedParameters["error_description"], let state = validatedParameters["state"] else {
return false
}
// ステートを検証します。
if !validateState(state: state) {
self.validatedResponse = ErrorResponse(url: incomingURL,error: "無効なステート", errorDescription: "リクエストのステートが無効です")
return true
}
self.validatedResponse = ErrorResponse(url: incomingURL, error: error, errorDescription: errorDescription)
return true
}
func getValidatedResponse() -> ValidatedResponse? {
return validatedResponse
}
}
// 検証応答
class ValidatedResponse {
// アプリを起動したユニバーサルリンクのURL
private(set) var url: URL
init (url: URL) {
self.url = url
}
}
// OAuth2仕様によるAmazon認可コード応答
class AuthResponse : ValidatedResponse{
private(set) var code : 文字列
private(set) var state: 文字列
//スコープをLogin with Amazonに返します。Alexaコンパニオンのフローには返しません。
private(set) var scope: String?
init(url:URL, code:String, state:String, scope: String?) {
self.code = code
self.state = state
self.scope = scope
super.init(url:url)
}
}
// OAuth2仕様によるエラー応答
class ErrorResponse : ValidatedResponse {
private(set) var error : 文字列
private(set) var errorDescription: 文字列
init(url: URL, error:String, errorDescription:String) {
self.error = error
self.errorDescription = errorDescription
super.init(url: url)
}
}
Androidの例
例: URLを取得する(Android)
バックエンドサービスにリクエストを送信して、AlexaアプリのURLとLWAフォールバックURLの両方を取得します。このリクエストは、RetroFitやその他のAndroidライブラリを使用するなど、任意の方法で実装できます。以下は、簡単なコードの例です。
fun doAppToApp(){
val appToAppUrls: AppToAppUrls = yourBackendService.getAppToAppUrls();
// この関数は別の例で示されています。
openAlexaAppToAppUrl(appToAppUrls.alexaAppUrl, appToAppUrls.lwaFallBackUrl)
}
data class AppToAppUrls(
@SerializedName("alexaAppUrl")
val alexaAppUrl: String,
@SerializedName("lwaFallBackUrl")
val lwaFallBackUrl : 文字列
)
interface BackendService {
fun getAppToAppUrls() : AppToAppUrls
}
例: URLを開く(Android)
この例では、アプリがAlexaアプリのURLを開き、Alexaアプリがインストールされていない場合は、LWAフォールバックURLを開きます。
以下に注意してください。
この例では、PackageManager
を使用して、Alexaアプリがインストールされていることと、そのAlexaアプリがアプリ間アカウントリンクを処理できるバージョンであることを確認します。
Alexaアプリがインストールされていない場合、またはアプリ間アカウントリンクを処理できない場合、アプリはLWAフローを使用してAmazon認可コードを取得する必要があります。LWAフォールバックURLの形式については、URLとエンドポイント を参照してください。
この例では、AndroidのACTION_VIEW
インテントを使用してURLを開きます。アプリ間URLはAlexaアプリで開かれ、LWAフォールバックURLはユーザーのデフォルトブラウザで開かれます。
private fun openAlexaAppToAppUrl(alexaAppUrl: String, lwaFallbackUrl: String){
if (AlexaAppUtil.isAlexaAppSupportAppLink(fragmentContext!!)) {
val alexaAppToAppIntent = getAppToAppIntent(alexaAppUrl);
startActivity(alexaAppToAppIntent)
} else {
val lwaAppToAppIntent = getAppToAppIntent(lwaFallbackUrl);
startActivity(lwaAppToAppIntent)
}
}
private fun getAppToAppIntent(appToAppUrl: String): Intent {
return Intent(Intent.ACTION_VIEW, Uri.parse(appToAppUrl))
}
/**
* Alexaアプリがインストールされていて、アプリ間アカウントリンクをサポートしているかを確認するためのユーティリティ。
*/
object AlexaAppUtil {
private const val ALEXA_PACKAGE_NAME = "com.amazon.dee.app"
private const val ALEXA_APP_TARGET_ACTIVITY_NAME = "com.amazon.dee.app.ui.main.MainActivity"
private const val REQUIRED_MINIMUM_VERSION_CODE = 866607211
/**
* Alexaアプリがインストールされていて、アプリリンクをサポートしている場合
*
* @param contextアプリケーションコンテキスト。
*/
@JvmStatic
fun doesAlexaAppSupportAppToApp(context: Context): Boolean {
try {
val packageManager: PackageManager = context.packageManager
val packageInfo = packageManager.getPackageInfo(ALEXA_PACKAGE_NAME, 0)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode > REQUIRED_MINIMUM_VERSION_CODE
} else {
packageInfo != null
}
} catch (e: PackageManager.NameNotFoundException) {
// Alexaアプリがインストールされていない場合
return false
}
}
}
例: アプリリンクの応答を処理する(Android)
この例では、Alexaアプリからアプリリンクへの呼び出しについて、アプリがそのパラメーターを検証して抽出します。
/**
* アプリ間アカウントリンクの結果ページのランディングアクティビティ。
*/
class AppToAppResultPageActivity : AppCompatActivity() {
private lateinit var statusText: TextView
private lateinit var goBackButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appLinkData = intent.data
setContentView(R.layout.app_to_app_result)
initializeComponents()
statusText.text = "Linking your account"
// AlexaアプリやLWAから返されたデータをバックエンドに送信し、
// スキル有効化APIを呼び出してアカウントをリンクします。
val accountLinking = linkAccountInBackend(appLinkData)
displayAccountLinkingStatus(result);
}
private fun initializeComponents() {
statusText = findViewById(R.id.text_status)
goBackButton = findViewById(R.id.btn_goback)
goBackButton.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
}
ステートの生成と検証
例: ステートヘルパー
この例では、アプリがリクエストと応答の間のステートを生成および検証しています。リクエスト強要(CSRF) を防ぐには、ステートを使用してリクエストを検証する必要があります。
import Foundation
import Security
class StateHelper {
private static let STATE_VALID_FOR = 3600
// Base64 (iOS + TimeStamp + Secure Random UUID)
private static let NUMBER_OF_PARAMETER = 3
static func generateState(session: Session)-> String {
var buffer = Data(count: 30)
let _ = buffer.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 30, $0)
}
let state = ("iOS." + String(Int64(Date().timeIntervalSince1970)) + "." + buffer.base64EncodedString()).encodeToURISafeBase64()
// 今後の検証に備えてセッションマネージャーにステートを保存します。
session.state = state
LocalSessionManager.saveSession(session: session)
return state
}
static func validateState(state: String) -> Bool {
guard let originalState = LocalSessionManager.loadSession()?.state else {
return false
}
if state != originalState {
return false
}
if let timeStampString = originalState.decodeFromURISafeBase64()?.split(separator: "."), timeStampString.count == NUMBER_OF_PARAMETER, let timeStamp = Int64(timeStampString[1]){
if Int64(Date().timeIntervalSince1970) - timeStamp <= STATE_VALID_FOR {
return true
}
}
if let session = LocalSessionManager.loadSession() {
session.state = nil
LocalSessionManager.saveSession(session: session)
}
return false
}
private init() {}
}
extension String {
func decodeFromURISafeBase64() -> String? {
let base64String = String(self.map {
// 安全でない文字を置換します。
character in
if character == "." {
return "+"
} else if character == "_" {
return "/"
} else if character == "-" {
return "="
}
return character
})
guard let data = Data(base64Encoded: base64String) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func encodeToURISafeBase64() -> String {
return String(Data(self.utf8).base64EncodedString().map{
// 安全でない文字を元に戻します。
character in
if character == "+" {
return "."
} else if character == "/" {
return "_"
} else if character == "=" {
return "-"
}
return character
})
}
}
ステップ5: Amazon認可コードをAmazonアクセストークンと交換する
ユーザーのAmazon認可コードを取得したら、それを使ってAmazonアクセストークンを取得する必要があります。Alexaサービスを呼び出してスキルを有効にし、アカウントリンクを完了するには、アクセストークンが必要です。
実装方法
以下の形式でHTTP POSTリクエストを行います。ユーザーに表示するエラーとメッセージの例については、Amazon認可コードとAmazonアクセストークンの交換時のエラー を参照してください。
リクエスト
POST /auth/o2/token HTTP/1.1
Host: api.amazon.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=authorization_code
&code=amazon authorization code
&client_id=yourClientId
&client_secret=yourClientSecret
&redirect_uri=yourRedirectUri
応答
HTTP 200
Content-Type: application/json
{
"access_token": "AmazonAccessToken",
"expires_in": "3600",
"refresh_token": "YourRefreshToken",
"token_type": "bearer"
}
LWAの場合にアクセストークンを取得する方法については、アクセストークンリクエスト 、アクセストークンレスポンス 、アクセストークンエラー を参照してください。
例: ユーザーのAmazonアクセストークンを取得する
この例では、ユーザーのAmazon認可コードをAmazonアクセストークンと交換します。
const axios = require('axios');
// Amazon OAuth token request in the backend
const OAuthRequests = (function () {
const header = {
"headers": {
"Content-Type": "application/json"
}
};
const accessTokenBody = function (amazonAuthCode, state) {
return {
"grant_type": "authorization_code",
"code": amazonAuthCode,
"redirect_uri": config.skillConfig.redirect_uri,
"client_id": config.skillConfig.client_id,
"client_secret": config.skillConfig.client_secret
};
};
return {
accessTokenRequest: (authcode, state) => {
return {
header: header,
body: accessTokenBody(authcode, state)
}
}
}
})();
// Amazon OAuthトークンサーバーにAmazonアクセストークンと更新トークンをリクエストします。
function getAccessTokenByAuthcode(amazonAuthCode, state) {
const accessTokenRequest = OAuthRequests.accessTokenRequest(amazonAuthCode, state);
return axios.post(config.endpoints.oauthEndpoint, accessTokenRequest.body, accessTokenRequest.header);
}
ステップ6: スキルを有効にしてアカウントリンクを完了する
ユーザーのAmazonアクセストークンを取得したら、バックエンドサーバーがAlexaスキル有効化API を呼び出してスキルを有効にし、アカウントリンクを完了することができます。
実装方法
Alexaスキル有効化API にリクエストを送信します。URLは、[ベースURL]/v1/users/~current/skills/{yourSkillId}/enablement
という形式で指定します。[ベースURL]
はユーザーの所在地の地域によって異なり、https://api.amazonalexa.com
、https://api.eu.amazonalexa.com
、https://api.fe.amazonalexa.com
のいずれかです。
使用するセキュリティプロバイダーは、アプリからアプリのバックエンドサーバーへの呼び出しを認可し、アプリにログイン中のユーザーについて認可コードを取得できるサーバーサイドAPIを提供する必要があります(つまり、サービスでのユーザーのアカウントについて認可コードを提供する必要があります)。 Alexaはこのコードをユーザーのアクセストークンと交換してユーザーのリソースにアクセスします。
ユーザーに表示するエラーとメッセージの例については、Alexaスキル有効化API を参照してください。
備考 •
LWA をセキュリティプロバイダーとして使用する場合、ユーザー
authCode
の取得に使用するLWAクライアントIDは、アプリとアプリ間のアカウントのリンク用にスキルを設定するときにAlexa開発者コンソールに表示されるAlexaクライアントIDとは異なることに注意してください。ユーザーの
authCode
の生成に、Amazonアクセストークンの取得(
ステップ5 )に使用したAlexaクライアントIDを使うことはできません。新しいユーザの
authCode
を生成するには、LWAのセキュリティプロファイルに関連付けられたLWAクライアントIDを使用します。
• スマートホームスキルで、スキルの
Alexaイベントを送る 権限を有効にしている場合、Lambda関数は
AcceptGrant
ディレクティブを処理する必要があります。そうでない場合、ユーザーがスキルを有効にしようとするとアカウントリンクが失敗します。詳細については、
Alexaイベントゲートウェイへのアクセス を参照してください。一般に、イベントを
イベントゲートウェイ に送信すると、現在の動作が引き続き実行されます。つまり、スキルが有効になった後は、スキルが
ディレクティブからアクセストークンを取得する ことができます。
スキルを有効にするには、以下のリクエストを行います。
リクエスト
以下のリクエストでは、ユーザーの所在地の地域に応じて、HOST
をapi.amazonalexa.com
、api.eu.amazonalexa.com
、api.fe.amazonalexa.com
のいずれかに設定します。
POST /v1/users/~current/skills/{skillId}/enablement HTTP/1.1
Host: api.amazonalexa.com, api.eu.amazonalexa.com, api.fe.amazonalexa.com
Content-Type: application/json
Authorization: "Bearer {Amazon Access Token}"
{
"stage": "スキルステージ",
"accountLinkRequest": {
"redirectUri": "https://yourRedirectURI",
"authCode": "認可サーバーから取得したユーザーの認可コードです。",
"type": "AUTH_CODE"
}
}
成功応答
HTTP 201
Content-Type: application/json
{
"skill": {
"stage": "スキルステージ",
"id": "スキルID"
},
"user": {
"id": "ユーザーID"
},
"accountLink": {
"status": "LINKED"
},
"status": "ENABLED"
}
ユーザーに対してスキルを無効にし、アカウントをリンク解除するには、Alexaスキル有効化APIにスキルの無効化とアカウントのリンク解除 リクエストを行います。
例: スキルを有効にしてアカウントリンクを完了する
この例では、アプリがスキルを有効にし、アカウントリンクを完了しています。
const _ = require('lodash');
const axios = require('axios');
const {config} = require('../config/skillconfig');
const {getAccessTokenByAuthcode} = require('./oauthrequests');
// バックエンドのAlexaスキル有効化APIへのリクエスト
const AlexaServiceRequest = (() => {
const header = (amazonAccessToken) => {
return {
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer " + amazonAccessToken
}
};
};
const body = (userAuthCode) => {
return {
"stage": config.skillConfig.stage,
"accountLinkRequest": {
"redirectUri": config.skillConfig.redirect_uri,
"authCode": userAuthCode,
"type": "AUTH_CODE"
}
}
}
return {
createEnablement: (amazonAccessToken, userAuthCode) => {
return {
header: header(amazonAccessToken),
body: body(userAuthCode)
}
}
}
})();
function createEnablementWithAccountLink(amazonAuthCode, userAuthcode, state) {
// Amazon OAuthコードをAmazonアクセストークンと交換します。
const tokenPromise = getAccessTokenByAuthcode(amazonAuthCode, state);
return tokenPromise.then((res) => {
if (!_.has(res, 'data.access_token')) {
throw Error("Amazonアクセストークンが無効です");
}
console.log(res);
const amazonAccessToken = res.data.access_token;
// Alexaスキル有効化APIを呼び出して有効化を作成します。
const createEnablementRequest = AlexaServiceRequest.createEnablement(amazonAccessToken, userAuthcode);
return sendPostRequests(config.endpoints.alexaServiceEndpoints, createEnablementRequest.body, createEnablementRequest.header);
});
}
// 有効化をAlexaスキル有効化APIにPostします。
function sendPostRequests(endpoints, body, header) {
var alexaServicePromises = []
// Alexaスキル有効化APIで使用されるユーザーのリージョン別の各エンドポイントについてPostします。
endpoints.forEach((endpoint)=> {
alexaServicePromises.push(axios.post(endpoint, body, header));
});
return new Promise((resolve, reject)=> {
var failures = 0;
alexaServicePromises.forEach((promise)=> {
promise.then((res)=> {
if (res.status == 201) {
resolve(res.data);
} else {
if (++failures == alexaServicePromises.length) {
reject(res.data);
}
}
}).catch((err)=> {
if (++failures == alexaServicePromises.length) {
reject(err.data);
}
})
})
});
}
ステップ7: アカウントリンクのステータスをアプリに表示する
アカウントリンクのステータスをアプリに表示して、ユーザーに知らせることができます。ユーザーの操作 やエラー のスクリーンショットを参考にしてください。
実装方法
アカウントリンクのステータスは、テーブルやページビューなど、さまざまなUIコンポーネントを使用して表示できます。
例: アカウントリンクのステータスを表示する
この例では、UIViewControllerを使ってアプリにアカウントリンクのステータスを表示します。
Xcode IDEで、メインのストーリーボードに移動して新しいビューコントローラーを追加します。このビューコントローラーのストーリーボードIDは、"linkingStatusScreen" です。
Cocoa Touchクラスのファイルを新たに追加して、UIViewControllerサブクラスを選択し、アイデンティティインスペクターでクラスファイルをUIViewControllerと接続します。
ヘッダー、アカウントリンクのステータスを表示するUILabel、このページを閉じるUIButton、アプリがAlexaサービスを呼び出す間ローディングアニメーションを表示するUIActivityIndicatorViewを追加します。
import UIKit
class LinkingStatusViewController: UIViewController {
@IBOutlet weak var header: UILabel!
@IBOutlet weak var status: UILabel!
@IBOutlet weak var closeButton: UIButton!
@IBOutlet weak var indicator: UIActivityIndicatorView!
var response : ValidatedResponse?
override func viewDidLoad() {
super.viewDidLoad()
if let res = self.response {
passValidatedResponse(validatedResponse: res)
}
}
private func updateView(header:String, status:String, animating: Bool,closeButton:Bool) {
if animating {
self.indicator?.startAnimating()
} else {
self.indicator?.stopAnimating()
}
self.indicator?.isHidden = !animating
self.header?.text = header
self.status?.text = status
self.closeButton.isEnabled = closeButton
if closeButton {
self.closeButton.backgroundColor = #colorLiteral(red: 0.1960784314, green: 0.6039215686, blue: 0.8392156863, alpha: 1)
} else {
self.closeButton.backgroundColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
}
}
}
extension LinkingStatusViewController {
func passValidatedResponse(validatedResponse: ValidatedResponse) {
if validatedResponse is AuthResponse {
let response: AuthResponse = validatedResponse as! AuthResponse;
updateView(header: "ステータス", status: "ロード中", animating: true, closeButton: false)
usingAuthCodeToGetAccessToken(amazonAuthCode: response.code)
} else if (validatedResponse is ErrorResponse) {
let response: ErrorResponse = validatedResponse as! ErrorResponse;
updateView(header: response.error, status: response.errorDescription, animating: false, closeButton: true)
} else {
updateView(header: "未知のリクエスト", status: validatedResponse.url.absoluteString, animating: false, closeButton: true)
}
}
// アカウントリンクを完了します。
private func usingAuthCodeToGetAccessToken(amazonAuthCode: String) {
guard let session = LocalSessionManager.loadSession(), session.sessionValid else {
updateView(header: "ステータス", status: "ユーザーがログインしていません", animating: false, closeButton: true)
return;
}
AlamofireHelper.completeAccountLinking(userAccessToken: LocalSessionManager.loadSession()!.accessToken, amazonAuthCode: amazonAuthCode, state: StateHelper.generateState(session: session)).responseJSON(queue: DispatchQueue.global(qos: .userInitiated), options: []) {
response in
if let status = response.response?.statusCode, 200..<300 ~= status {
DispatchQueue.main.sync {
self.updateView(header: "ステータス", status: "アカウントリンクが接続されました", animating: false, closeButton: true)
}
return
} else {
DispatchQueue.main.sync {
self.updateView(header: "失敗", status: "アカウントをリンクできませんでした", animating: false, closeButton: true)
}
}
}
}
}
// アカウントリンクのステータスページを開きます。
let initialViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "linkingStatusScreen") as! LinkingStatusViewController
initialViewController.response = validatedResponseself.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
ステップ8: スキルでアクセストークンを使用する
ユーザーがスキルを有効にしてAlexaをサービスにリンクすると、スキルに送信されるリクエストにユーザーのアクセストークンが含まれるようになります。スキルコードは、リクエストからアクセストークンを取得して検証し、それを使ってリソースサーバーから必要なユーザー情報を取得する必要があります。
実装方法
アクセストークンの検証方法と使用方法の詳細については、以下のスキルタイプ別の説明を参照してください。
エラー
このセクションでは、アカウントリンクやスキル有効化が失敗したときに発生するエラーについて説明します。エラーが発生すると、アプリにエラーメッセージが表示されます。以下の図に一例を示します。このセクションでは、他のエラーメッセージについても説明します。
エラーの説明
このセクションでは、アプリ間アカウントリンクの接続時にユーザーが遭遇する可能性のあるエラーについて説明します。アプリは、アカウントリンクが失敗したことをユーザーに通知し、エラーメッセージを示す必要があります。このセクションでは、エラーメッセージおよび考えられる原因について説明します。良質のユーザーエクスペリエンスを提供するため、ここで示すエラーメッセージを使用することをお勧めします。
アプリ間アカウントリンクのフローで、Alexaからアプリへのエラーが発生する可能性があるのは以下の3つの時点です。
Amazon認可コード取得時のエラー
アプリがAmazon認可コードの取得を試みたときにエラーが発生した場合、Alexaアプリがアプリへのユーザーのリダイレクト先として使用するredirect_uri
に、OAuth 2.0仕様 の説明に従ってエラークエリーパラメーターが格納されます。AlexaアプリとLWAフォールバックはどちらもOAuth 2.0仕様に準拠しているので、同じエラーコードがアプリに返されます。Amazon認可コードを取得するリクエストが失敗した理由については、error_description
クエリパラメーターを確認し、返されたエラーコードについてOAuth 2.0仕様の説明を参照してください。
invalid_request
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
リクエストで、state
、scope
、response_type
のいずれかが指定されていません。
unauthorized_client
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
指定したclient_id
には、ユーザーにアカウントリンクの同意を求めるアクセス権がありません。
access_denied
ありません(アクセスを拒否しているのはユーザーの方なので、ユーザーにはエラーメッセージが表示されません)。
ユーザーが自分のアカウントリンクのリクエストを拒否しました。
unsupported_response_type
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
パラメーターresponse_type
がcode
と一致しません。
invalid_scope
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
scope
パラメーターが無効です。
server_error
アカウントリンクの接続中に、Alexaで予期しないエラーが発生しました。後でもう一度試してください。
予期しないサーバーエラー。
temporarily_unavailable
アカウントリンクの接続中に、Alexaで一時的なエラーが発生しました。後でもう一度試してください。
一時的なサーバーエラー。
Amazon認可コードとAmazonアクセストークンの交換時のエラー
Amazon認可コードをAmazonアクセストークンと交換する際にエラーが発生した場合、OAuth 2.0仕様 の説明に従って、Amazonトークン・サーバーが ”400Bad Request” ステータスコードを返し、ボディのJSON応答にエラーパラメーターが含まれます。Amazonアクセストークンを取得するリクエストが失敗した理由については、JSON応答のerror_description
パラメーターを確認し、返されたエラーコードについてOAuth 2.0仕様の説明を参照してください。
invalid_request
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
client_id
、client_secret
、grant_type
、code
のいずれかが指定されていません。
invalid_client
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
client_id
またはclient_secret
が無効です。
invalid_grant
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
リクエストのcode
(Amazon認可コード)が無効です。
unauthorized_client
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
指定したclient_id
には、ユーザーのアカウントをリンクすることについて同意を求める権限がありません。
unsupported_grant_type
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
リクエストのgrant_type
がauthorization_code
ではありません。
Alexaスキル有効化APIの呼び出し時のエラー
アプリがAlexaスキル有効化API を呼び出してユーザーのアカウントを有効にし、リンクしようとしてエラーが発生した場合、APIはリクエストが失敗した理由を示すステータスコードを返します。それ以外の場合、APIはHTTP 2XX
ステータスコードで応答します。スキル有効化リクエストが失敗した理由については、JSON応答のmessage
パラメーターを確認してください。
400 Bad Request
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
skill_id
、stage
、accountLinkingRequest.authCode
、accountLinkingRequest.redirectUri
、accountLinkingRequest.Type
のいずれかが指定されていません。
アプリのアクセストークンと更新トークンのペアをリクエスト中に、エラーが発生しました。
403 Forbidden
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
Amazonアクセストークンが無効です。
Amazonアクセストークンに、スキルの有効化についてのユーザーの同意が含まれていません。
Amazonアクセストークンが、リクエストで指定されたskill_id
に属していません。
ユーザーには、リクエストで指定されたskill_id
のスキルを有効にする権限がありません。
404 Not Found
Alexaに接続してアカウントをリンクする際に問題が発生しました。後でもう一度試してください。
500 Server Error
アカウントリンクの接続中に、Alexaで予期しないエラーが発生しました。後でもう一度試してください。
予期しないサーバーエラー。
アカウントをアプリのOAuthサーバーにリンク中に問題が発生しました。
ベストプラクティス
アプリ間アカウントリンクの実装時には、次のベストプラクティスを参考にしてください。
アプリ間アカウントリンクの見つけやすさ
アプリからアプリ間アカウントリンクを簡単に使用するには、次のベストプラクティスを参考にしてください。
クリック数を少なくする
アカウントリンクフローを開始するために必要なクリック数をできるだけ少なくしてください。アプリのホーム画面からアカウントリンクフローに移動するまでに、3回以上クリックする必要はありません。アプリのホーム画面から始まる、一般的なフローの例です。
フローの例1
ユーザーは次の操作を行います。
アプリのハンバーガーメニューをクリックします。
Alexaとリンク をクリックします。
許可 をクリックして、アプリからアカウントリンクのフローを開始します。
Alexaアプリ内でアカウントリンクに同意します。
アプリにリダイレクトされます。
フローの例2
ユーザーは次の操作を行います。
アプリのハンバーガーメニューをクリックします。
サードパーティ統合 (または類似の文言)をクリックします。
Alexaとリンク をクリックします。
許可 をクリックして、アプリからアカウントリンクのフローを開始します。
Alexaアプリ内でアカウントリンクに同意します。
アプリにリダイレクトされます。
Alexaとの統合がわかるようにする
ユーザーが初めてアプリを使用するときに、Alexaとの統合について知らせます。たとえば、アプリのセットアップの最後に、Alexaロゴ付きの画面を追加して、アプリ間アカウントリンクのフローにユーザーを誘導します。
ユーザーがAlexaロゴをクリックすると、アプリ間アカウントリンクのフローが始まります。ユーザーがアカウントリンクをスキップしたい場合は、メニュー画面で後からリンクできることを知らせます。
音声アシスタントの設定を確認する
アプリに音声アシスタントに関する設定がある場合は、その設定からアカウントリンクが開始できるようにしてください。
ユーザビリティ
アプリ間アカウントリンクを簡単に使用するには、次のベストプラクティスを参考にしてください。
アカウントリンクの正確な状態を表示する
アプリに正しいボタン(リンク またはリンク解除 )を表示するには、Alexaスキル有効化APIに対してアカウントリンクとスキルのステータスの取得 リクエストを行い、特定のユーザーのスキル有効化とアカウントリンクのステータスを取得します。ただし、これはアプリ間アカウントリンクを使用してアカウントをリンクしたユーザーに対してのみ正常に応答します。したがって、ユーザーがスキルを無効にしてアカウントのリンクを解除したときに通知を受け取るには、スキル無効化イベント にも対応することを推奨します。ユーザーがアカウントをリンクしているかどうかを確認するために、バックエンドに存在するAlexaトークンペアを頼りにすることはできません。
明確なメッセージを提供する
アプリ間アカウントリンクのエクスペリエンスを考慮し、すべてのフローにおいてユーザーに明確なメッセージを提供します。ユーザーがアカウントリンクの途中でキャンセルしたり、タイムアウトしたりするケースを適切に処理します。エラーに関して明確な情報を提供します。
アカウントリンクに必要な情報がそのほかにある場合は、ユーザーをAlexaアプリやLWAにリダイレクトする前にたずねます(追加の詳細情報、同意、PINなど)。
ユーザーのリクエストが金融取引や個人情報が関係する処理の場合、リクエストを実行する前に、事前に設定されたセキュリティの質問に答えるようにユーザーに求めます。
ブランドガイドラインに従う
アプリのユーザーインターフェースは、Amazon開発者のためのAlexaブランドガイドライン に従ってデザインします。これにより、サービスをAlexaと使用するためにアカウントリンクが求められることが一目でわかります。
リンク解除
アカウントリンク済みのユーザーのリンク解除と、リンク解除をユーザーに知らせるためのベストプラクティスです。
アプリ内でリンク解除を実装する
アプリ内からアカウントリンクを解除する方法をユーザーに提供する必要があります。そのためには、アプリ間アカウントリンクのフロー中にアプリが受け取ったAmazonのアクセストークンペアを保持して、後のアプリからのリンク解除のために使用します。ユーザーに対してスキルを無効にし、アカウントをリンク解除するには、Alexaスキル有効化APIにスキルの無効化とアカウントのリンク解除 リクエストを行います。
リンク済みのユーザーで、アプリ間アカウントリンクを使用していない場合は、リンク解除 ボタンを押したときにAlexaアプリのスキルページに遷移して、ユーザーがスキルを無効 にできるようにします。Alexaアプリのスキルのページのリンクは、次の例のようになります。
Android :https://alexa.amazon.com/spa/index.html#skills/dp/{skillAsin}
iOS :https://alexa.amazon.com/spa/index.html#skills/dp/{skillAsin}
フォールバック(Alexaアプリがインストールされていない場合) : ユーザーをhttps://www.amazon.co.jp/dp/{skillAsin}
にリダイレクトします。
スキルのAmazon Standard Identification Number(ASIN)を確認するには、Alexaスキルストア でスキルを表示します。ASINは、URL内にある10桁の英数字のIDです。たとえば、URLがwww.amazon.com/gp/product/A1A1A1A1A1
の場合、ASINはA1A1A1A1A1
となります。
リンクしていないユーザーに知らせる
リンクしていないユーザーに、アカウントリンク機能について知らせます。ユーザーに知らせるには、プッシュ通知、アプリを新しいバージョンに更新した後の「新機能」のポップアップ画面、アプリストアのリリースノート、ブログ投稿、電子メールなど、さまざまな方法があります。
バックエンドコード
アプリ間アカウントリンクがスムーズに機能するように、次のベストプラクティスを参考にしてください。
バックエンドでURLを組み立てる
ユーザーのAmazon認可コードを取得する コードでは、可能な限り、AlexaアプリのURLとLWAフォールバックURL をバックエンドサーバーで組み立てから、組み立てたURLをアプリに渡すことをお勧めします。これにより、アプリを再ビルドせずにパラメーター(ステージなど)をすぐに変更できます。
リダイレクトURIが機能することを確認する
ステップ4 でLWA auth_code
を返すときに、LWAがユニバーサルリンクとアプリリンクをトリガーしない場合があります。ユーザーがアカウントリンクを断念しないように、LWA認証ページと同じウェブビュー内で開かれている場合は、redirect_uri
がアカウントリンクリクエストに確実に応えるようにすることをお勧めします。この機能を実装するには、ウェブページがステップ4 のredirect_uri
で受信するauth_code
をバックエンドサーバーに送信して、ステップ5 とステップ6 を実行します。最後に、成功画面(ステップ7 )をウェブページに直接表示します。
テストのガイドライン
スキルはまず、すべてのスキルに適用される認定の要件 に適合する必要がありますが、それに加え、以下の点のテストを行うことをお勧めします。
Alexaアプリがインストールされている場合に、Alexaアプリのフロー を使って、アプリからアカウントリンクを開始したときに正常に完了できることを確認します。Androidでは、AlexaアプリのURLを開くときに別のアプリで開く セレクターが表示される場合は、お使いのバージョンのAlexaアプリに無効なアプリリンクがあることを意味します。この問題を解決するには、Alexaアプリを削除して再インストールします。
Alexaアプリがインストールされていない場合に、LWAフロー を使って、アプリまたはウェブサイトからアカウントリンクを開始したときに正常に完了できることを確認します。LWAのフローをサポートしない実装の場合は、アプリまたはウェブサイトが正常なエラーメッセージをユーザーに返す必要があります。
アカウントリンクが失敗した場合、アプリやウェブサイトが正常なエラーメッセージをユーザーに返すことを確認します。
アカウントリンクが完了すると、アプリを使用してアカウントリンクを解除できます。また、アカウントリンク状況に応じて、正しいボタン(リンク またはリンク解除 )が表示されるはずです。
また必ずスキルのベータテスト を行ってください。ベータテストを有効にするときは、ベータテスターを追加 することを忘れないでください。
スキルは公開されるまで、stage
値をdevelopment
に設定してAlexaアプリのURL とAlexaスキル有効化API を使用する必要があります。スキルの公開時にlive
に変更します。
よくある質問
以下は、アプリから開始する場合のアプリ間アカウントリンクについてのよくある質問です。
質問: ユーザーのモバイルデバイスにAlexaアプリがインストールされていない状態を、どのように確認できますか?
iOSで確認する場合の例については、 例:URLを開く をステップ4: ユーザーのAmazon認可コードを取得する で参照してください。
質問: 複数のスキルを持っています。すべてのスキルでアカウントを一度にリンクできますか?
ユーザーの信用を保護するため、アプリ間アカウントリンクには、スキルごとにユーザーの同意が必要です。ユーザーのアカウントを複数のAlexaスキルにリンクする場合、ユーザーは複数回リンクフローを操作する必要があります。
質問: スキルが既に有効で、アカウントが既にリンクされていることはどのように確認できますか?
Alexaスキル有効化APIに対してアカウントリンクとスキルのステータスを取得する リクエストを行います。このAPIを呼び出すときは、次のアクセストークンのいずれかを使用する必要があります。
ユーザーのアプリ間アカウントリンクを設定したときに受け取ったトークン。
(スマートホームスキルのみ)相互認証後にAcceptGrant
ディレクティブから受け取ったトークン。
質問: スキルが既に有効で、アカウントが既にリンクされている場合どうなりますか?
Alexaスキル有効化APIに対してスキルの有効化とアカウントリンク リクエストを行うと、ユーザーのアカウントが再リンクされ、成功応答 が返されます。
質問: アカウントリンクが完了したときに開発者が通知を受け取るにはどうすればよいですか?
アカウントリンクイベント のサブスクリプションを行ってください。
質問: ユーザーがアプリで現在ログインしているアカウントとは別のユーザーアカウントをAlexaとリンクさせたい場合、どうすればいいですか?
Alexaをほかのアカウントとリンクするには、まずほかのアカウントでアプリにログインし、アプリ内からアカウントリンクを開始します。アカウントリンクフローの中でアカウントを切り替えるオプションをアプリに表示することもできます。
質問: ユーザーがAlexaを使用しておらず、Alexaアプリをインストールしていない場合はどうすればいいですか?
ユーザーがAmazonアカウントを持っているがAlexaは使用していない場合、Amazonの認証情報を使ってLWAページにサインインし、リンクリクエストに同意します。ユーザーがAmazonアカウントを持っていない場合、LWA画面で新規のAmazonアカウントを作成し、スキルのアカウントリンクを行います。ただし、ユーザーがAlexa機能を使用するには、Alexa搭載デバイスまたはAlexaアプリが必要です。
質問: ユーザーがアカウントリンクを取り消すにはどうすればよいですか?
スキルを無効にすると、ユーザーのアカウントのリンクが解除されます。スキルを無効にするには、「アレクサ、 {skill name}を無効にして」と話しかけるか、スキルの詳細ページからスキルを削除します。 ユーザーに対してスキルを無効にし、アカウントをリンク解除するには、Alexaスキル有効化APIにスキルの無効化とアカウントのリンク解除 リクエストを行います。
質問: 公開しているスキルをユーザーが無効にすると、開発者にどのようなことが発生しますか? 開発者には通知されますか?
公開しているスキルをユーザーが無効にしたときに通知を受け取るには、スキル無効化イベント のサブスクリプションを行います。スキルが無効になった場合、開発者はアクセストークンを取り消し、ユーザーのAmazonアクセストークンを削除する必要があります。Alexaもサービスから提供されたアクセストークンを削除し、Amazonアクセストークンを取り消します。
質問: スキルに権限が必要な場合、 アプリ間アカウントリンクの接続時に権限も取得できますか?
権限 (デバイスのアドレスやユーザープロファイル情報など)は現在、このアカウントリンクフローでは取得できません。権限が必要なインテントでユーザーがスキルを呼び出す場合、実行時に権限カードでユーザーにプロンプトを出して、必要な権限を求める必要があります。
ただし、ユーザーがもう1つのアプリ間アカウントリンクフロー(アプリ間アカウントリンク(Alexaアプリから開始する場合) )を使用してアカウントをリンクする場合、権限を付与するよう求めるプロンプトがユーザーに対して出されます。そのフローを使用して必要な権限を付与できます。
質問: モバイルデバイスにAlexaアプリがインストールされていない状態を、どのように確認できますか?
AlexaアプリがiOSデバイスまたはAndroidデバイスにインストールされているかどうかを確認する方法については、 例:URLを開く をステップ4: ユーザーのAmazon認可コードを取得する で参照してください。
質問: サービスからユーザーアカウントの認可コードを取得するにはどうすればよいですか?
認可サーバーを使用して、ユーザーの認可コードを生成する必要があります。従来のアカウントリンクフローでは、この認可コードは、ユーザーがサービスにログインし、Alexaとのリンクに同意するステップで生成されていました。アプリ間アカウントリンク(アプリから開始)の場合は、ユーザーはすでにログインしているため、OAuthプロバイダーからユーザーの新しい認可コードをプログラムで生成する必要があります。ユーザーは、再度ログインして同意する必要はありません。
最終更新日: 2024 年 04 月 23 日