【Python】FlaskでTwiterからログインする機能(OAuth認証)を実装する

FlaskでのOauth認証を実装したくて色々とググっていたのですが、中途半端というかコピペでそのまま動作する例が見つからなかったので、ちょっと作ってみました。

前準備

今回の例では、自分のTwitterアカウントのAPIが必要になるので、前もって取得しておく必要があります。

昔はアカウントさえあれば、一瞬で取得できたのですが、最近は厳格化の流れで電話番号とかが必要になります。またツイッターのAPIの取得の流れはコロコロ変わるので、最新情報を追っているサイトで確認してください。

現在の最新版は恐らくこれだと思います。

https://qiita.com/kngsym2018/items/2524d21455aac111cdee

OAuth認証とは何か

まずOAuth認証について一応触れておくと、超簡単に言うと他のアプリのログイン情報を引っ張ってくることのできる機能です。有名なのが、ツイッターの診断系のアプリとかですね。↓のようなやつです。

ざっくりとした仕組みについては以下の記事で説明されているので、オススメです。

https://colo-ri.jp/develop/2010/09/twitter_api_oauth.html

FLASKでOAuth認証を実装する

それでは本題に入っていきます。今回はPythonのWebフレームワークである「Flask(フラスコ)」を使ってOAuth認証によるログイン機能を実装していきたいと思います。

PythonでのOAuth認証の実装には、requests-oauthlibというライブラリを使用します。あまりライブラリに依存するのは良くありませんが、OAuth認証の仕組みは結構複雑なので、今はとりあえず動くことを優先します。

動くコードが↓になります。全体はGitに挙げているので、ダウンロードするなりして確認してください。

<app.py>

from flask import Flask, render_template, session
from flask import url_for, redirect, request

#自作ライブラリの呼び出し
import twitter_oauth
import db

# いつもの
app = Flask(__name__)
# Sessionの暗号化に使う任意の文字列(適当でOK)
app.secret_key = 'A0Zr98j/3yX Rnaxaixaixai~XHH!jmN]LWX/,?RT'


# トップ画面
@app.route('/')
def index():
    # セッションが既にあればアカウントIDを表示、なければログインボタンを表示
    if 'user_name' in session:
        user_name = session['user_name']
    else:
        user_name = []
    return render_template("index.html", user_name=user_name)


# Twitterの認証画面にリダイレクトする
@app.route("/oauth_register")
def redirect_oauth():
    # Twitter Application Management で設定したコールバックURLsのどれか
    oauth_callback = request.args.get('oauth_callback')
    # Twitter認証画面のURLを生成する
    callback = twitter_oauth.get_twitter_request_token(oauth_callback)
    # 認証画面にリダイレクトする
    return redirect(callback)


@app.route('/callback')
def execute_userinfo():
    """[認証画面から返されてきた情報を取得して処理する関数]
    Return [ホーム画面('/')にリダイレクトする]
    """
    oauth_token = request.args.get('oauth_token')
    oauth_verifier = request.args.get('oauth_verifier')

    # リクエストトークンからアクセストークンを取得
    access_token = twitter_oauth.get_twitter_access_token(oauth_token, oauth_verifier)

    # 既に登録していないかチェック、無ければ新規にデータをテーブルにINSERTする
    # 既に登録済みであれば、その情報を返す
    user_data = db.search_user(access_token)
    if user_data is False:
        # 新規ユーザーはアクセストークンの情報をデータベースに保存
        db.register_userinfo(access_token)
        # 登録してからもう一回ユーザーデータを取得
        user_data = db.search_user(access_token)
    # 返された情報をSessionに保存する
    session['user_name'] = access_token['screen_name']
    session['user_id'] = access_token['user_id']
    session['oauth_token'] = access_token['oauth_token']
    session['oauth_token_secret'] = access_token['oauth_token_secret']
    # トップ画面にリダイレクトする
    return redirect(url_for('index'))


@app.route("/logout")
def logout():
    # セッションに渡しているデータを削除
    session.pop('user_name', None)
    session.pop('user_id', None)
    session.pop('oauth_token', None)
    session.pop('oauth_secret', None)
    return redirect(url_for('index'))

# サーバー起動
app.run(debug=True)

<twitter_oauth.py>

from urllib.parse import parse_qsl
from requests_oauthlib import OAuth1Session
import json

# ここに自分のAPI鍵を入力してください
consumer_key = ''
consumer_secret = ''
access_key = ''
access_secret = ''

# base urls for oauth
base_url = 'https://api.twitter.com/'
request_token_url = base_url + 'oauth/request_token'
authenticate_url = base_url + 'oauth/authenticate'
access_token_url = base_url + 'oauth/access_token'
base_json_url = 'https://api.twitter.com/1.1/%s.json'
user_timeline_url = base_json_url % ('statuses/user_timeline')

def get_twitter_request_token(oauth_callback):
    """[このアプリと連携しますか?という認証画面のURLを生成する関数]]]
    Arguments:
        oauth_callback {[コールバックURL]} -- [デベロッパーツールで指定したURL]
    Returns:
        [認証画面] -- [自分のAPI鍵を付与した認証画面を発行し、アクセスをリダイレクトさせる]
    """

    twitter = OAuth1Session(consumer_key, consumer_secret)
    response = twitter.post(request_token_url, params={'oauth_callback': oauth_callback})
    request_token = dict(parse_qsl(response.content.decode("utf-8")))

    # リクエストトークンから認証画面のURLを生成
    authenticate_endpoint = '%s?oauth_token=%s' \
        % (authenticate_url, request_token['oauth_token'])

    request_token.update({'authenticate_endpoint': authenticate_endpoint})
    return request_token['authenticate_endpoint']


def get_twitter_access_token(oauth_token, oauth_verifier):
    """[ユーザーの渡してきたリクエストトークンを使ってアクセストークンをツイッター  から取得する関数]
    Keyword Arguments:
        oauth_token {[str]} -- [ユーザーのトークン1] (default: {oauth_token})
        oauth_verifier {[str]} -- [ユーザーのトークン2] (default: {oauth_verifier})
    Returns:
        [str] -- [アクセストークン]
    """

    # twitterのデータベース?にアクセスする準備
    twitter = OAuth1Session(
        consumer_key,
        consumer_secret,
        oauth_token,
        oauth_verifier,
        )

    # ユーザーの返してきたアクセストークンと自分のAPI鍵セットした状態でツイッターに殴り込みをかける
    response = twitter.post(access_token_url, params={'oauth_verifier': oauth_verifier})

    # レスポンスの中にアクセストークンを取得する(これがお目当てのもの)
    access_token = dict(parse_qsl(response.content.decode("utf-8")))
    return access_token

<db.py>

import sqlite3

# データベースの基本設定
dbpath = 'crypto.sqlite'


def search_user(access_token):
    """[アクセストークンからユーザー情報を検索して一致するものがあればTrue
    なければFalseを返す関数]
    Returns:
        [dict] -- [辞書型のアクセストークン]
    Returns:
        [List] -- [userのデータ]
    """
    
    c = sqlite3.connect(dbpath, check_same_thread=False)
    cur = c.cursor()
    user_token = access_token['oauth_token']
    user_secret = access_token['oauth_token_secret']
    user_id = access_token['user_id']
    query = "select * from user_info where user_token=" + "'" + user_token + "'" + " and" + \
        ' user_secret=' + "'" + user_secret + "'" + " and" + ' user_id=' + user_id
    cur.execute(query)
    user_data = cur.fetchall()
    c.close()
    if user_data == []:
        return False
    else:
        return user_data[0]


def register_userinfo(access_token):
    """[アクセストークンから取得したユーザー情報をテーブルにINSERTする関数]
    Returns:
        [dict] -- [辞書型のアクセストークン]
    Returns:
        [type] -- [description]
    """

    c = sqlite3.connect(dbpath, check_same_thread=False)
    cur = c.cursor()
    user_oauth_token = access_token['oauth_token']
    user_oauth_token_secret = access_token['oauth_token_secret']
    user_id = int(access_token['user_id'])
    user_screen_name = access_token['screen_name']
    provider = 'twitter'
    insert_query = f"'{user_id}','{user_screen_name}','{provider}','{user_oauth_token}', '{user_oauth_token_secret}'"
    sql = 'insert into user_info(user_id,user_screen,provider,user_token,user_secret) VALUES (' + \
        insert_query + ');'
    cur.execute(sql)
    c.commit()
    c.close()

<templates/index.html>

<html>
<h1>FlaskでSNSログインを実装する</h1>
<p>
{% if user_name == [] %} Twitterでログインする 
{% else %} 
こんにちは{{user_name}}さん!
ログアウトする
{% endif %}
</html>

OAuth認証の基本的な流れとしては。まず↓の画像のようなよく見るTwitterの認証画面自体はTwitter側が作ってくれているので、こちらでhtmlを作ったりとか特に何かする必要はありません。

ですが、このwebアプリ製作者側がこの画面をログイン認証に使用するためには、自分自身のアカウントの「API Key」と「API Secret」が必要になります。

Twitter側は私たちが個々に発行した「API Key」によって「どのアプリケーションが、どれだけAPIを利用しているか?」「どのユーザーがどのアプリケーションにアカウントを接続しているか?」などを把握しています。

なので、まず我々はOauth認証などのツイッターのAPI機能を使いたい場合はまず、API鍵を取得する必要があります。そして、それを使うことでAPIの認証画面にアクセスすることができるようになります。

この認証画面で、アプリを使用するユーザー側がアカウントのID(またはメアド)とパスワードを入力すると、「アクセストークン」というものが発行されます。

そしてこのユーザーが発行したアクセストークンをこちら(アプリケーション側)が受け取り、それを使用してツイッターにアクセスし、そのユーザーのアカウントの権限(任意のテキストをツイートしたりタイムラインを取得するなど)の一部を行使できます。

なのでアプリケーション側がそのアクセストークンを悪用すれば、レイバンのサングラス広告のようなスパムが勝手にツイートできます。

コールバックURLはツイッターのAPIドキュメントの方で設定する必要があります。今回はテストネットなので、http://127.0.0.1:5000/callbackにしてください。

認証が完了すると、そのユーザーのリクエストトークンがcallbackurlの後ろにくっついてくるので、それを↓の部分で回収しています。

oauth_token = request.args.get('oauth_token')
oauth_verifier = request.args.get('oauth_verifier')

そして、リクエストトークンからアクセストークンを取得し、それが既に登録されている場合はスルー、されていない場合は登録するという処理を行います。

アクセストークンやパスワードといった情報はハッシュ化して保管しておくのはセオリーですが、今回はめんどくさかったのでしていません。

関連記事:Pythonで分かりやすく学ぶハッシュ関数とブロックチェーン

そして、ログイン状態を維持するために、セッションで情報を保管しています。↓

# 返された情報をSessionに保存する
session['user_name'] = access_token['screen_name']
session['user_id'] = access_token['user_id']
session['oauth_token'] = access_token['oauth_token']
session['oauth_token_secret'] = access_token['oauth_token_secret']

ちなみにSessionを暗号化するために任意の文字列の設定(最初のapp.secret_key =~'の部分)が必要で、それがないと以下のようなエラーを吐かれるので、注意してください↓

RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.

そして、トップ画面にリダイレクトしたら、セッション情報が保存されているので、連携したツイッターアカウントのIDが表示されています。

最後のログアウト部分はセッションを削除してトップ画面にリダイレクトしているだけです。

長くなってしまったので、最後のOauth認証の流れをまとめると

①自分のAPI鍵の情報を付けたTwitter認証画面のURLを生成する

②ユーザーがその認証画面で認証すると、サーバー側にそのリクエストトークンが発行される

③そのリクエストトークンを使用して、アクセストークンを取得する

④アクセストークンを使用して、連携したユーザーのアカウント情報(名前やツイート)を取得する

こんな流れになります。ここではユーザー登録のために使うので、この記事では取得したアクセストークンをデータベースに保管する工程を追加しています

sqlite3を使ったPythonからのデータベースの操作は以下の記事を参考にしてください

関連記事:Pythonでデータベース(DB)を作成・操作できる「sqlite3」の使い方を分かりやすく解説する

終わり

このコードを実行すると認証画面にアクセスし、適当なアカウントでログイン認証を完了すると、そのアカウントのIDがトップ画面に表示されます。

他にもツイート情報やフォローリムーブもできるので、投稿時間の分析や適当な糞診断の作成ができるようになりますし、個人でログイン制のWebサービスを作るときなんかにも一々自分でユーザーのメアドとパスワードを保持していると、何かの弾みで流出すると大変なことになるので、Oauthにしてその辺を丸投げすることも可能です。

参考資料

https://qiita.com/mikan3rd/items/686e4978f9e1111628e9

https://syncer.jp/Web/API/Twitter/REST_API/

https://qiita.com/zaburo/items/5091041a5afb2a7dffc8

https://blanktar.jp/blog/2017/11/python-flask-auth0.html

関連記事

flaskによるWebアプリ開発①~簡単なWebページを表示させてみる

flaskによるWebアプリ開発②~テンプレートエンジン(Jinja2)で動的Webページを作る

flaskによるWebアプリ開発③~GETとPOSTとformタグでのデータの送受信

flaskによるWebアプリ開発④~SQLAlchemyを使ってデータベースと連動させる

flaskによるWebアプリ開発⑤~url_forと変数による動的URLの作成方法

【Python】Flask+SQLAlchemyを使って、「ひとこと掲示板」を作る

【Python】FlaskでMatplotlibでプロットしたグラフを表示させる

プログラミングの独学はとても難しい


プログラミングは小学校の義務教育にも導入され始めており、これから社会人として生きていく上でプログラミングはもはや出来て当たり前、出来なれば論外というエクセルレベルの必須スキルになりつつあります。そしてそういう話を聞いて参考書なりを購入して独学でプログラミング勉強しようと思っている人も少なくないでしょう。しかしプログラミングを独学で勉強し始めようと思うものの



・「分からない箇所で詰まって挫折してしまった」

・「勉強する時間が足りない」

・「ネットの記事だと情報が断片的でよくわからない」

・「コードのエラーの原因が分からない」



という壁にぶち当たって、プログラミングの勉強を止めてしまう方が少なくありません。独学でプログラミングを勉強してる時間のほとんどはつまづいている時間です。実際僕も最初のころ独学でプログラミングを勉強していた頃はエラーの原因が分からず丸1日を不意にしてしまった・・・そんな苦い経験がありました。それで僕は一度はプログラミングの学習を諦めてしまいましたが、就活で現実を知る中で



「プログラミングを勉強して、いずれフリーランスとして自由な生き方がしたい」

「エンジニアとして若いうちから高収入を得たい」



という強い気持ちから一念発起して「侍エンジニアのwebサービスコース」に申し込み、プロのエンジニアの方に対面でマンツーマンでPythonによるWebサービス作り方とWeb技術の基本を教えてもらい、ようやくプログラミングが理解でき、今ではエンジニアとしてそこそこの暮らしができるようになりました。





侍エンジニアでは、とりあえずプログラミングやインターネットの基本を知っておきたい人から、HTML・cssなどでWebサイトやWebアプリを作ってみたい人やPythonを勉強してデータサイエンティストやAIエンジニアになりたい人まで幅広いニーズに応えた様々なコースが用意されています。



IT業界と言ってもエンジニアの仕事はプログラミング言語次第でサーバーから機械学習・ディープラーニングまで多種多様ですし、侍エンジニアの無料レッスン(カウンセリング)を受けてみて、自分のやりたいITの仕事は何なのか?を見つけるのがエンジニアへの第一歩になります。ちなみに今侍エンジニアの無料レッスンを受けると1000円分のAmazonギフト券がもらえるので、試しに受けてみるだけもお得です。


自分は半端に独学やオンラインスクールで勉強して金と時間を無駄にするくらいなら、リアルのプログラミングスクールに通ってしっかりプログラミングを勉強した方がいいと思います。ちなみに今、侍エンジニアに申し込むと、25歳以下の学生の方であれば、受講料が20%OFFになるので超お得です。


そして、プログラミングは大勢で授業を受けたり漫然とオンライン学習をするよりも自分が分からない箇所をピンポイントでプロの講師に直接質問して、ちゃんと納得するというスタイルの方がお金は確かに少し掛かりますが、独学で学ぶよりも絶対にモノになります。


シェアする

  • このエントリーをはてなブックマークに追加

フォローする

   侍エンジニア塾       
侍エンジニアの無料体験レッスンを予約する -->