カスタムスキルコードでアクセストークンを検証して使用する


カスタムスキルコードでアクセストークンを検証して使用する

ユーザーがスキルを有効にしてAlexaアカウントを他のサービスにリンクしたら、スキルに送信されるリクエストにはユーザーのアクセストークン(accessToken)が含まれるようになります。スキルのサービスにこのアクセストークンを確認して使用する処理を追加します。

このドキュメントでは、カスタムスキルのアカウントリンク処理について説明します。スマートホームやビデオスキルを開発している場合は、スマートホームとビデオのスキルコードでアクセストークンを検証して使用するを参照してください。

アカウントリンクを必要とするリクエストを決める

カスタムスキルでは認証(つまり有効なアクセストークン)をすべてのリクエストで必須とすることもできますし、一部のリクエストでのみ必須とすることもできます。どちらにするかは、スキルのデザインと機能によって異なります。

たとえば、タクシー予約スキルでは配車リクエストにユーザー認証が必要ですが、サービスが特定の都市で利用できるかどうかをたずねるだけであれば必要ありません。このため、このスキルのコードでは以下の処理を行います。

  • OrderCarインテント用のハンドラーで有効なaccessTokenを確認し、そのトークンを使ってユーザーのタクシー予約でのプロフィールを取得し、車を手配します。
  • RideHailerAvailabilityByCityインテント用のハンドラーではタクシー予約サービスの公開情報を検索して、応答を返します。このハンドラーではaccessTokenの確認は必要ありません。
  • LaunchRequest用のハンドラーでは、スキルの案内を提供してユーザーに何をしたいかをたずねます。このハンドラーではaccessTokenの確認は必要ありません。

認証の必要なリクエストの場合は、以下の処理が必要です。

  1. リクエストからアクセストークンを取得する
  2. トークンを検証してユーザーを認証する
  3. トークンが存在しないか無効な場合、アカウントリンクカードとユーザーにとって分かりやすい音声出力を返します。

認証を必要としないインテントの場合、これらの手順を省略して通常の応答だけを返すことができます。

必要なアカウントリンク

一部のスキルでは、ユーザーがアカウントリンクを行っていない場合に、代わりの機能を提供しないものがあります。この場合、すべてのリクエストでアクセストークンを確認し、ユーザーがアカウントをリンクしていない場合には適切なプロンプトとアカウントリンクカードを返す必要があります。

ユーザー: アレクサ、タクシー予約を開いて。

Alexa: タクシー予約へようこそ。このスキルを使用するには、タクシー予約アカウントが必要です。Alexaアプリに情報をお送りしましたので、Alexaアプリを開き、リンクをクリックしてタクシー予約アカウントをAlexaにリンクしてください。
セッションが終了します。

すべてのリクエストでアカウントリンクが必須の場合、必ずユーザーがアカウントリンクなしでスキルを有効にすることを許可してくださいオプションをオフにしてください。

任意のアカウントリンク

スキルにアカウントリンクが不要で役に立つ機能が含まれている場合、ユーザーがアカウントリンクなしでスキルを有効にすることを許可してくださいオプションをオンにしてください。こうすることで、ユーザーはスキルを有効にするときにアカウントリンクをスキップできるため、ユーザーの手間が軽減されスキルを試してもらいやすくなります。ユーザーがアカウントをリンクせずにスキルを有効にするを参照してください。

アカウントリンクを奨励する場合の1つの方法として、ユーザーアカウントのリンクのメリットを説明して、続行するかどうかをたずねることをおすすめします。この処理を行うには、LaunchRequestで返すようこそメッセージが最適です。例:

ユーザー: アレクサ、タクシー予約を開いて。

Alexa: タクシー予約へようこそ。タクシー予約アカウントをお持ちでしたら、配車をリクエストできます。今アカウントをセットアップされますか?
ユーザー: はい。 (これによりビルトインのAMAZON.YesIntentが送信されます)

スキルはアカウントリンクカードを送信します。詳細はトークンが無効または存在しない場合にユーザーに応答するを参照してください。
Alexa: わかりました。Alexaアプリに情報をお送りしましたので、Alexaアプリを開き、リンクをクリックしてタクシー予約アカウントをAlexaにリンクしてください。
セッションが終了します。

ユーザーが「いいえ」と答えた場合、スキルはアカウントを必要としない代わりの機能を提供できます。

ユーザー: アレクサ、タクシー予約を開いて。

Alexa: タクシー予約へようこそ。タクシー予約アカウントをお持ちでしたら、配車をリクエストできます。今アカウントをセットアップされますか?
ユーザー: いいえ。

Alexa: わかりました。タクシー予約アカウントをお持ちでなくても、道案内と交通情報を提供できます。サービスを利用できる都市もお知らせできます。具体的な都市名か、最近利用できるようになった都市についてお答えできます。どちらがいいですか?
ユーザーとの対話が続きます...

リクエストからアクセストークンを取得する

ユーザーがアカウントをリンクすると、ユーザーのアクセストークン(accessToken)は、そのaccessTokenが有効な間、スキルに送信されるすべてのリクエストに含まれるようになります。アカウントをまだリンクしていない場合、accessTokenプロパティはリクエストに含まれません。

Authorization code grantフローを設定しており、既にリンクされているユーザーの更新トークンが無効または期限切れであるために、Alexaサービスが認可サーバーから新しいトークンのペアを取得できない場合、accessTokenプロパティはスキルに送信されるリクエストに含まれません。このシナリオでは、Alexaサービスが既存の無効または期限切れのトークンを削除し、ユーザーのAlexaアプリにプッシュ通知と再リンクのホームカードを送信して、ユーザーにアカウントを再リンクさせます。

トークンは、contextオブジェクトにあるuserオブジェクトのaccessTokenプロパティで取得できます。contextオブジェクトはすべてのリクエストに含まれます。accessTokenにはcontext.System.user.accessTokenでアクセスできます。

この例でのユーザーのaccessTokenは「Atza|<accessToken>」です。実際のアクセストークンは通常これよりも長くなります。簡潔に表現するために、リクエストの一部のオブジェクトは省略しています。

{
  "version": "1.0",
  "session": {},
  "context": {
    "AudioPlayer": {
      "playerActivity": "IDLE"
    },
    "Display": {},
    "System": {
      "application": {
        "applicationId": "amzn1.ask.skill.1111111-2222-3333-4444-5555555"
      },
      "user": {
        "userId": "amzn1.ask.account.XXXXXXXXXX",
        "accessToken": "Atza|<accessToken>"
      },
      "device": {},
      "apiEndpoint": "https://api.amazonalexa.com",
      "apiAccessToken": "<apiToken>"
    }
  },
  "request": {}
}

accessTokenをすべてのリクエストに含まれるapiAccessTokenと混同しないでください。apiAccessTokenは、デバイスアドレスAPIプログレッシブ応答APIなどのAlexa Skills Kit APIで使用されます。

次の例では、リクエストハンドラーのaccessTokenにアクセスする方法を示します。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

const OrderCarIntentHandler = {

    //...

    handle(handlerInput){
        // このインテントではユーザーのタクシー予約プロフィールと支払い情報を
        // 取得するためにアクセストークンが必要です。

        // アクセストークンはContextオブジェクトにあります。ハンドラーに渡される
        // HandlerInputオブジェクトでリクエストに
        // アクセスします。

        var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;

        // ...
    }
};

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

public class OrderCarIntentHandler implements RequestHandler {
    // ...

    @Override
    public Optional<Response> handle(HandlerInput handlerInput) {

        // このインテントではユーザーのタクシー予約プロフィールと支払い情報を
        // 取得するためにアクセストークンが必要です。

        // アクセストークンはContextオブジェクトにあります。ハンドラーに渡される
        // HandlerInputオブジェクトでリクエストに
        // アクセスします。

        String accessToken = handlerInput
                .getRequestEnvelope()
                .getContext()
                .getSystem()
                .getUser()
                .getAccessToken();

        // ...ハンドラーの処理が続きます
    }
}

アクセストークンを検証する

認証の必要なリクエストの場合、コードは少なくとも以下の2点を確認する必要があります。

  1. accessTokenが存在することを確認します。
  2. トークンがリソースサーバーの有効なユーザーを表すことを確認します。

トークンが存在することを確認する

accessTokenが存在することを確認します。accessTokenプロパティがリクエストに含まれない場合、ユーザーがアカウントリンクをまだ完了していないか、ユーザーがリンクしたアカウントのトークンが期限切れまたは無効であることを表します。

Alexa Skills Kit SDK for Node.jsでは、トークンが存在しない場合、useraccessTokenプロパティ(handlerInput.requestEnvelope.context.System.user.accessTokenなど)がundefinedを返します。Alexa Skills Kit SDK v2 for Javaでは、トークンが存在しない場合、User.getAccessToken()nullを返します。

トークンが有効なことを確認する

accessTokenが存在する場合、このアクセストークンがリソースサーバーのユーザーを識別することを確認します。トークンが無効とみなされる理由はさまざまです。以下はその例です。

  • ユーザーがサービスでアカウントを削除またはキャンセルした場合。たとえば、Alexaユーザーがタクシー予約でアカウントリンクをセットアップしてから、タクシー予約アカウントをキャンセルした場合などです。この時点で、Alexaサービスが保存したトークンは、存在しないユーザーを識別することになります。
  • トークンの有効期限が切れ、Alexaサービスが新しいトークンを取得できなかった場合。これは、認可サーバーが更新トークンを提供しない場合のAuthorization code grantで発生する可能性があります。更新トークンをサポートしないImplicit grantを使用している場合にも発生する可能性があります。

トークンが有効な場合、リクエストを正常に処理します。トークンを使い、必要に応じてリソースサーバーからデータにアクセスできます。タクシー予約の例では、スキルはタクシー予約サービスからユーザーのプロフィールと支払い情報を取得し、車を手配してユーザーに確認を返します。詳細については、応答を返すを参照してください。

トークンが無効または存在しない場合にユーザーに応答する

認証の必要なリクエストでアクセストークンが存在しないか無効な場合、以下を含む応答を返します。

  • この機能を使うためにはアカウントリンクが必要なことを、ユーザーに説明する出力音声テキストを返します。音声テキストでは、アカウントリンクのメリットを簡潔に説明します。
  • アカウントリンクカード。これはユーザーにアカウントリンクを行うよう伝える特別な種類のカードです。Alexaアプリに表示する場合は、認証画面のURIへのリンクが表示されます。これにより、ユーザーはこのカードから直接アカウントリンクを開始できます。

JSONの応答に、typeLinkAccountを指定したcardプロパティを含めます。

{
    "version": "1.0",
    "sessionAttributes": {},
    "response": {
      "outputSpeech": {
        "type": "PlainText",
        "text": "車を手配するには、タクシー予約アカウントが必要です。Alexaアプリを使用してAmazonアカウントとタクシー予約アカウントをリンクしてください。"
      },
      "card": {
        "type": "LinkAccount"
      },
      "shouldEndSession": true
    }
}

cardTitleなどその他のcardプロパティはすべて無視されます。

以下の例は、Alexa Skills Kit SDK for Node.jsAlexa Skills Kit SDK v2 for Javaを使ってアカウントリンクカードを返す方法を示しています。

クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。

const OrderCarIntentHandler = {

  // ...

  handle(handlerInput){

    // このインテントではユーザーのタクシー予約プロフィールと支払い情報を
    // 取得するためにアクセストークンが必要です。

    // アクセストークンはContextオブジェクトにあります。ハンドラーに渡される
    // HandlerInputオブジェクトでリクエストに
    // アクセスします。

    var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;

    if (accessToken == undefined){
        // リクエストにはトークンが含まれなかったため、ユーザーに
        // リンクするよう伝えてアカウントリンクカードを返します。
        var speechText = "車を手配するには、タクシー予約アカウントが必要です。" +
                    "Alexaアプリを使用してAmazonアカウントとタクシー予約アカウントを" +
                    "リンクしてください。";

        return handlerInput.responseBuilder
            .speak(speechText)
            .withLinkAccountCard()
            .getResponse();
    } else {

        // トークンを使ってユーザーのプロフィールにアクセスします。ここでもトークンが有効な
        // タクシー予約ユーザーを表すことを確認する必要があります。

        // ...

    }
  }
};


クリップボードにコピーされました。

このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。

public class OrderCarIntentHandler implements RequestHandler {

    // ...

    @Override
    public Optional<Response> handle(HandlerInput handlerInput) {

        // このインテントではユーザーのタクシー予約プロフィールと支払い情報を
        // 取得するためにアクセストークンが必要です。

        // アクセストークンはContextオブジェクトにあります。ハンドラーに渡される
        // HandlerInputオブジェクトでリクエストに
        // アクセスします。

        String accessToken = handlerInput
                .getRequestEnvelope()
                .getContext()
                .getSystem()
                .getUser()
                .getAccessToken();

        if (accessToken != null) {
            // メソッドを呼び出してトークンを検証し、ユーザーのタクシー予約プロフィールを
            // 取得し、車を手配します。
            return orderCarForUser(accessToken, handlerInput);
        } else {
            // リクエストにはトークンが含まれなかったため、ユーザーに
            // リンクするよう伝えてアカウントリンクカードを返します。

            String speechText = "車を手配するには、タクシー予約アカウントが必要です。"
                    + "Alexaアプリを使用してAmazonアカウントとタクシー予約アカウントを"
                    + "リンクしてください。";

            // 出力音声とアカウントリンクカードを含む応答を作成します。        
            return handlerInput.getResponseBuilder()
                    .withSpeech(speechText)
                    .withLinkAccountCard()
                    .build();
        }
    }
}

ほとんどの場合、この応答でセッションが終了します。これは、ユーザーがアカウントリンクを完了しないとリクエストを続行できないためです。ユーザーエクスペリエンスを向上させるには、関連ユーザーデータを永続データストアに保存して、ユーザーがアカウントをリンクさせた後、続きからスキルに戻れるようにします。

スキルに認証を必要としないインテントが含まれる場合、ユーザーに別の質問をしてセッションを開いたままにしてもかまいません。


このページは役に立ちましたか?

最終更新日: 2023 年 12 月 18 日