前回、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が保存して利用する流れです。
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を利用します。
Alexaスキルを作る
TwitterApplicationManagement
まずはTwitterApplicationManagementにログインし、連携アプリケーションを一意に識別するためのConsumserKeyとConsumerSecretを取得します。
必要事項を入力してアプリケーション登録を行います。このとき **Callback URL
の値を必ず入力します。**値は何でも良いのですが、この入力が無いとOAuthの認証処理が正常に行われません。
登録が完了すると「Key and Access Tokens」のタブにConsumerKey
とConsumerSecret
の値が表示されるのでメモしておきます。Twitter側の準備はこれで完了です。
OAuthのトンネリングサーバー
今回の肝となるOAuth2.0と1.0をトンネリングするサーバーを作成します。と言っても、github上に有用なサンプルプログラムが公開されているのでそちらを使います。
developing-alexa-skills-solutions/5_accountLinking/
serverディレクトリ内のserver.rb
を実行すればそのまま使えるはずです。が、私の環境ではthinサーバーのhttps通信でエラーが出てしまいました。
ということで私は少し修正加えて利用しています。参考までに修正部分を載せておきます。
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を利用し、サーバー証明書を設定するように修正しました。
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を作成します。基本的には前回と同じです。
異なる点は設定メニューで 「アカウントリンク」 を設定することです。
ユーザーにアカウントの作成や既存のアカウントへのリンクを許可しますか?
で 「はい」 を選択します。
認証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を取得します。
'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_KEYとCONSUMER_SECRETを登録します。
これで準備は完了です。
実行してみる
まず、トンネリング用のサーバーを起動します。
bundle exec rackup
次に、前回 の手順と同じように開発用のAlexaスキルとして実機に導入します。
スキルの画面から**「有効にする」** を実行するとAlexa Skills Kitで設定した認証用URL=トンネリングサーバーにリダイレクトされます。
そこからTwitterのOAuth認証用ページに飛ばされます。**「連携アプリを認証」**をクリックするとaccess_tokenとaccess_token_secretが発行されます。それをトンネリングサーバーが受け取り、値を結合してAlexa Skills Kitに戻します。