Flask Python Webアプリケーション プログラミング

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

この記事は約18分で読めます。

 

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にしてその辺を丸投げすることも可能です。

 

 

参考資料

 

[Python] OAuth認証でTwitter連携/ログインを実装する - Qiita
概要PythonでTwitter連携を実装しようとしてみたところ、あんまり例がなかったのでつくってみましたドキュメントを読めば分かるという人はこちら → Implementing Sign i…
Twitter REST APIの使い方
Twitterが提供するREST APIの使い方をまとめています。
Flaskの簡単な使い方 - Qiita
Pythonの軽量フレームワーク。Flaskの最低限の使い方。とりあえず自分が使う範囲を調べてメモ。後はQuickStartをやれば一通りわかる。Djangoな人はこちらもどうぞ。準備と簡単…
Not Found - Blanktar
Not Found

 

 

関連記事

 

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でプロットしたグラフを表示させる

 

 

 

 

 


プログラミング・スクレイピングツール作成の相談を受け付けています!

クラウドワークス・ココナラ・MENTAなどでPython・SQL・GASなどのプログラミングに関する相談やツール作成などを承っております!

過去の案件事例:

  • Twitter・インスタグラムの自動化ツール作成
  • ウェブサイトのスクレイピングサポート
  • ダッシュボード・サイト作成
  • データエンジニア転職相談

これまでの案件例を見る

キャリア相談もお気軽に!文系学部卒からエンジニア・データサイエンティストへの転職経験をもとに、未経験者がどう進むべきかのアドバイスを提供します。


スポンサーリンク
/* プログラミング速報関連記事一覧表示 */
ミナピピンの研究室

コメント

タイトルとURLをコピーしました