Ruby
Node.js
OAuth
TwitterAPI
Alexa

AlexaスキルでTwitterのアカウントリンクを行う

前回、TwitterAPIを使ってツイートするAlexaスキルを作りました。
TwitterでつぶやくAlexaスキルを作る

この時作ったスキルはaccess_tokenとaccess_token_secretをプログラム中に埋め込むため特定のTwitterアカウント専用のスキルでした。

今回はAlexaスキルからTwitterにアカウントリンクを行う「正攻法」でTwitterAPIを利用してみます。

アカウントリンク

通常Alexaスキルから他のサービスを利用する場合は、Alexa Skills Kitから外部サービスの利用を許可するためのアカウントリンク を行います。

Alexa Skills KitでアカウントリンクをOAuth2.0で行います。これにより連携サービスからaccess_tokenが発行され、それをAlexa Skills Kitが保存して利用する流れです。
無題のプレゼンテーション (2).png

TwitterはOAuth1.0

しかしこのアカウントリンク、Twitterのツイートには使えません。TwitterAPIでは以下の機能を利用する場合には OAuth1.0で認可を得る必要があります。

  • 投稿
  • ストリーミング接続
  • ユーザー検索
  • 位置情報の利用
  • ダイレクトメッセージ

このため認可方式の異なるAlexa Skills KitとTwitterAPIはそのままではアカウントリンクを行えません。

じゃあどうするの?

Alexa Skills KitとTwitterAPIの間にOAuthをトンネリングするサーバーを作ります。

このサーバーはAlexaスキルから受け取ったパラメータをTwitterAPIにそのまま渡します。

OAuth1.0のサービス利用にはaccess_tokenとaccess_token_secretの2つのキーが必要であり、TwitterAPIからも最終的にこの2つの値を受け取ります。しかし、AlexaSkillsKitはaccess_tokenしか記憶しません。

そこでトンネリングサーバーは、TwitterAPIから受け取ったaccess_tokenとaccess_token_secretを結合して、ひとつのaccess_tokenかのようにAlexaスキルに返却します。

Alexaスキル利用時には保存されたaccess_tokenの値を分割することで、元のaccess_tokenとaccess_token_secretを復元し、TwitterAPIを利用します。
無題のプレゼンテーション (3).png

Alexaスキルを作る

TwitterApplicationManagement

まずはTwitterApplicationManagementにログインし、連携アプリケーションを一意に識別するためのConsumserKeyとConsumerSecretを取得します。

必要事項を入力してアプリケーション登録を行います。このとき Callback URLの値を必ず入力します。値は何でも良いのですが、この入力が無いとOAuthの認証処理が正常に行われません。
2018-01-20_21h39_59.png

登録が完了すると「Key and Access Tokens」のタブにConsumerKeyConsumerSecretの値が表示されるのでメモしておきます。Twitter側の準備はこれで完了です。
2018-01-20_21h42_45.png

OAuthのトンネリングサーバー

今回の肝となるOAuth2.0と1.0をトンネリングするサーバーを作成します。と言っても、github上に有用なサンプルプログラムが公開されているのでそちらを使います。

developing-alexa-skills-solutions/5_accountLinking/

serverディレクトリ内のserver.rbを実行すればそのまま使えるはずです。が、私の環境ではthinサーバーのhttps通信でエラーが出てしまいました。

ということで私は少し修正加えて利用しています。参考までに修正部分を載せておきます。

server.rb
get '/oauth/request_token' do
    session[:consumer_key]         = consumer_key
    session[:consumer_secret]      = consumer_secret
    request_token = consumer.get_request_token oauth_callback: CALLBACK_URL
    session[:request_token]        = request_token.token
    session[:request_token_secret] = request_token.secret
    session[:state]                = params[:state]
    session[:client_id]            = params[:client_id]
    session[:vendor_id]            = vendor_id
    #redirect_uriの取得処理を追加
    session[:redirect_uri]         = params[:redirect_uri]
    puts request_token
    redirect request_token.authorize_url
end
#(中略)
def redirect_url(access_token)
    "#{session[:redirect_uri]}" \ #Alexa Skills Kitから受け取ったurlを使う
    "#state=#{session[:state]}" \
    "&access_token=#{access_token.token},#{access_token.secret}" \
    "&client_id=#{session[:client_id]}" \
    '&token_type=Bearer'
end

サンプルプログラムでは、Twitter側でユーザーが許可した後の戻り先(redirect_url)が固定で設定されていましたが、Alexaスキルからのアカウントリンク開始時にパラメータredirect_uriに設定されているため、その値を利用します。

また、server.rbのポイントは

"&access_token=#{access_token.token},#{access_token.secret}" \

の部分です。最初に説明したとおりAlexaスキルへ返却するパラメータaccess_tokenに対して、Twitterから取得したaccess_tokenとaccess_token_secretを,で結合することであたかも一つの値のように返却します。



config.ruについても、サーバーにWEBRickを利用し、サーバー証明書を設定するように修正しました。

config.ru
require 'webrick'
require 'webrick/https'
Rack::Handler::WEBrick.run Sinatra::Application,{
    :Port => 443,
    :BindAddress => '0.0.0.0',
    :SSLEnable => true,
    :SSLCertificate => OpenSSL::X509::Certificate.new(File.open('./certs/server.crt').read),
    :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.open('./certs/server.key').read)
}

Alexa Skills Kit

Alexa Skills Kitを作成します。基本的には前回と同じです。

異なる点は設定メニューで 「アカウントリンク」 を設定することです。
アマゾン アプリ 開発者ポータル11.png

ユーザーにアカウントの作成や既存のアカウントへのリンクを許可しますか?「はい」 を選択します。

認証URLには、今回作成するトンネリングサーバーのURLを以下の形式で入力します。

https://サーバ名/oauth/request_token/consumer_key=取得したConsumerKey&consumer_secret=取得したConsumerSecret

クライアントIDはTwitterAPIに通知される値で、このAlexaスキルを表す任意の名前を入力します。

アクセス権限「Implicit Grant」 を選択します。

プライバシーポリシーURLには適当な値を入力します。(前述したサーバーのサンプルプログラムには/policyのパスでhtmlファイルが準備されています。)

AWS Lambda

最後にAlexaスキルの実行部分をAWS Lambdaに登録します。登録手順は前回とほとんど一緒です。

ソースコードとモジュール化したzipファイルはGitHubにアップロードしています。
https://github.com/quotto/TweetByAlexa-AccountLink

ソースコードも前回とほぼ同じですが、今回はAlexa Skills Kitから送られてくるセッション情報this.event.session.user.accessTokenを分割し、access_tokenとaccess_token_secretを取得します。

index.js
    'GoOutTweet' : function() {
        // アクセストークンの取得
        var accessToken = this.event.session.user.accessToken;
        if(accessToken == null) {
            // トークン未定義の場合はユーザーに許可を促す
            this.emit(':tellWithLinkAccountCard',AccountLinkMessage);
            return;
        }

        var accessKey = accessToken.split(',');
        var dt = calculateJSTTime();
        var stringTime = dt.getFullYear() + "年" + (dt.getMonth()+1) + "月" + dt.getDate() + "日 " + dt.getHours() + "時" + dt.getMinutes() + "分" + dt.getSeconds() + "秒 ";
        var message = stringTime + "に出かけました。(Alexaスキルでつぶやいています)";
        Twitter.postTweet(message,accessKey[0],accessKey[1]).then(()=>{
            this.emit(':tell','つぶやきました。<say-as interpret-as="interjection">いってらっしゃい。</say-as>');
        },(error)=>{
            console.log(error);
            this.emit(':tell',ErrorMessage);
        })
    },

AWS Lambdaの環境変数にはCONSUMER_KEYCONSUMER_SECRETを登録します。
2018-01-20_17h06_59.png

これで準備は完了です。

実行してみる

まず、トンネリング用のサーバーを起動します。

bundle exec rackup

次に、前回 の手順と同じように開発用のAlexaスキルとして実機に導入します。

スキルの画面から「有効にする」 を実行するとAlexa Skills Kitで設定した認証用URL=トンネリングサーバーにリダイレクトされます。
2018-01-20_16h46_12.png

そこからTwitterのOAuth認証用ページに飛ばされます。「連携アプリを認証」をクリックするとaccess_tokenとaccess_token_secretが発行されます。それをトンネリングサーバーが受け取り、値を結合してAlexa Skills Kitに戻します。
2018-01-20_16h47_05.png

このページが表示されればアカウントリンク成功です。ツイートできるようになります。
2018-01-20_16h47_28.png