OAuthでTwitter連携!! 誰でもTwitterAPIが利用可能に
どうも、現役エンジニアのこじまるです。
今回の記事はTweet検索ツールの続編になります。
以前Twitter APIを使って、Tweet検索ツールを作成しました。このツールはユーザが指定したワードでTweet検索し、そのTweetにいいね、リツイートをすることができます。ただ、いいね・リツイートは筆者のアカウントでしかできません。そのため、他のユーザでもいいね・リツイートができるように変更してみました。
Twitter APIのOAuth1.0の仕組みがいまいち分からない
Twitter APIのOAuth1.0での実装ってどのようにするんだろう?
この記事では、このような悩みを解決します。
※本記事ではFlaskを使ってOAuth1.0を実装しましたが、想像以上に苦労しました。なので、今後学習される方のために認証の流れを丁寧に説明します。
OAuthでTwitter連携!! 誰でもTwitterAPIが利用可能に
OAuth1.0の流れ
今回の実装ではTwitter APIを実行するまでに、OAuth1.0の認可の仕組みを導入しています。
具体的には、①~④の流れでOAuth1.0の処理を行います。アクセストークンを取得して、クライアントがTwitter APIを利用できる状態にします。そして、⑤では取得したアクセストークンを利用し、Twitter APIを実行します。
①事前処理
事前処理として、Twitter Developer PortalよりCallback URLを登録します。このCallback URLは、Twitter ServerがリダイレクトさせるURLになります。
②リクエストトークンを取得する
UserがViewに表示される下記ボタンの押下により、ViewはServerにリクエストトークンの取得を要求します。
リクエストトークンの取得のために、Serverでは下記の形式でTwitter Serverにリクエストを送出します。ここで、oauth_callbackは必須パラメータで、①でTwitter Developer Portalに登録したCallback URLと一致する必要があります。
POST https://api.twitter.com/oauth/request_token { 'oauth_callback':'http://192.168.10.107:5000/main' }
@app.route('/signin',methods=["POST"]) def postSignIn(): consumer_key, consumer_secret = session_manager.getConsumerKey() twitter = OAuth1Session(consumer_key, consumer_secret) endpoint = "https://api.twitter.com/oauth/request_token" oauth_callback = "http://192.168.10.107:5000/main" response = twitter.post(endpoint, params={'oauth_callback':oauth_callback}) token_dict = dict(parse_qsl(response.content.decode('utf-8'))) authentication_endpoint = "https://api.twitter.com/oauth/authenticate?oauth_token={}".format(token_dict['oauth_token'])
③ユーザをリダイレクトする
次に、②のPOSTリクエストで取得したoauth_tokenを使用して、Twitter Serverにユーザ認証を要求します。
GET https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx
上記のリクエストを送出するために、リダイレクトを使って実装しました。具体的には、ResponseのStatus codeを302, ヘッダのLocationフィールドを上記のURLに設定し、Viewにレスポンスを返しています。
@app.route('/signin',methods=["POST"]) def postSignIn(): ... authentication_endpoint = "https://api.twitter.com/oauth/authenticate?oauth_token={}".format(token_dict['oauth_token']) response = jsonify() response.status_code = 302 response.headers['location'] = authentication_endpoint return response
ブラウザがURLにアクセスすると、下記のようにTwitterの認可ページが表示されます。筆者のツールの場合、いいね・リツイートを行うUserの情報を入力し、Twitter Serverに認証を依頼します。
認証に成功すると、②で指定したCallback URLにリダイレクトします。
④リクエストトークンをアクセストークンに変換する
③でリダイレクトしたURLのクエリストリングには、oauth_token, oauth_verifierが付与されています。
http://192.168.10.107:5000/main?oauth_token=xxxxxxxxxxxx&oauth_verifier=xxxxxxxxxxxx
リクエストトークンをアクセストークンに変換する下記のTwitter APIでoauth_token, oauth_verifierが必須となっています。そのため、それらの情報を抽出し、下記のリクエストボディに与えます。
POST https://api.twitter.com/oauth/access_token { 'oauth_token':'xxxxxxxxxxxx', 'oauth_verifier':'xxxxxxxxxxxx' }
上記のリクエストが成功することで、oauth_token,oauth_token_secret,...が取得できます。
def generateAccessToken(self,oauth_token, oauth_verifier): twitter = OAuth1Session(self.cosumer_key, self.cosumer_secret) access_endpoint = "https://api.twitter.com/oauth/access_token" response = twitter.post(access_endpoint, params={'oauth_token':oauth_token,'oauth_verifier':oauth_verifier}) access_token_dict = dict(parse_qsl(response.content.decode('utf-8'))) self.access_token = access_token_dict['oauth_token'] self.access_secret = access_token_dict['oauth_token_secret']
⑤サインイン後の処理
④で取得した、oauth_token,oauth_token_secretを使って、Twitter APIを実行します。
@app.route('/searchTweet',methods=["POST"]) def getTweet(): print('Enter getTweet') ... consumer_key, consumer_secret, access_token, access_secret = session_manager.getAllKey() global twitter twitter = OAuth1Session(consumer_key, consumer_secret, access_token, access_secret) endpoint = "https://api.twitter.com/1.1/search/tweets.json?{}&{}&{}&{}&{}".format(q,lang,result_type,count,entities) res = twitter.get(endpoint)
参考
https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1
https://syncer.jp/Web/API/Twitter/REST_API/#comment
[Python] OAuth認証でTwitter連携/ログインを実装する - Qiita
結果
Twitter ToolをOAuth1.0で認証できるようにしました(^^)!!
— cojimaru (@cojimaru3) March 27, 2021
ユーザ認証を使用しているので、Twitterに登録しているアカウントなら誰でもTweet検索を行って、いいね・Retweetを行うことができます✨ pic.twitter.com/JgJ4NG1CI2