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

前回、TwitterAPIを使ってツイートするAlexaスキルを作りました。
[TwitterでつぶやくAlexaスキルを作る](https://qiita.com/quotto/items/7fbda7efc6b57dc03992)

この時作ったスキルは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](https://qiita-image-store.s3.amazonaws.com/0/183744/960780cd-4a2a-63a1-2079-111cf350b5d3.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](https://qiita-image-store.s3.amazonaws.com/0/183744/47e2f815-9e78-1062-ea78-981e41fc0668.png)


# Alexaスキルを作る

## TwitterApplicationManagement
まずは[TwitterApplicationManagement](https://apps.twitter.com)にログインし、連携アプリケーションを一意に識別するためのConsumserKeyとConsumerSecretを取得します。

必要事項を入力してアプリケーション登録を行います。このとき **`Callback URL`の値を必ず入力します。**値は何でも良いのですが、この入力が無いとOAuthの認証処理が正常に行われません。
<img width="400" alt="2018-01-20_21h39_59.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/5a52e267-4c48-f82c-4f6a-00fe6e1e89fc.png">

登録が完了すると「Key and Access Tokens」のタブに`ConsumerKey`と`ConsumerSecret`の値が表示されるのでメモしておきます。Twitter側の準備はこれで完了です。
<img width="400" alt="2018-01-20_21h42_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/022772bb-deeb-47dd-5eaf-35be3d2958d6.png">


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

[developing-alexa-skills-solutions/5_accountLinking/](https://github.com/bignerdranch/developing-alexa-skills-solutions/tree/master/5_accountLinking/solution)

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

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

```ruby: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`のポイントは

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

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

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

```ruby: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を作成します。基本的には[前回](https://qiita.com/quotto/items/7fbda7efc6b57dc03992)と同じです。

異なる点は設定メニューで **「アカウントリンク」** を設定することです。
<img width="599" alt="アマゾン アプリ 開発者ポータル11.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/ee1d124a-3307-360e-2940-ac5beb79334f.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に登録します。登録手順は[前回](https://qiita.com/quotto/items/7fbda7efc6b57dc03992)とほとんど一緒です。

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

ソースコードも[前回](https://qiita.com/quotto/items/7fbda7efc6b57dc03992)とほぼ同じですが、今回はAlexa Skills Kitから送られてくるセッション情報`this.event.session.user.accessToken`を分割し、access_tokenとaccess_token_secretを取得します。

```javascript: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_KEY**と**CONSUMER_SECRET**を登録します。
<img width="832" alt="2018-01-20_17h06_59.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/01dc2cbc-58fe-d5b4-5889-4c77156b5486.png">

これで準備は完了です。

# 実行してみる
まず、トンネリング用のサーバーを起動します。

```shell
bundle exec rackup
```

次に、[前回](https://qiita.com/quotto/items/7fbda7efc6b57dc03992) の手順と同じように開発用のAlexaスキルとして実機に導入します。

スキルの画面から**「有効にする」** を実行するとAlexa Skills Kitで設定した認証用URL＝トンネリングサーバーにリダイレクトされます。
<img width="507" alt="2018-01-20_16h46_12.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/1f1203e2-6b76-2222-207b-eeb13da2ac38.png">

そこからTwitterのOAuth認証用ページに飛ばされます。**「連携アプリを認証」**をクリックするとaccess_tokenとaccess_token_secretが発行されます。それをトンネリングサーバーが受け取り、値を結合してAlexa Skills Kitに戻します。
<img width="525" alt="2018-01-20_16h47_05.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/8dce17cc-c844-6d49-b12c-da503f017d83.png">

このページが表示されればアカウントリンク成功です。ツイートできるようになります。
<img width="482" alt="2018-01-20_16h47_28.png" src="https://qiita-image-store.s3.amazonaws.com/0/183744/13e4c908-16c2-fe7f-3ad8-605d1560a167.png">