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にしてその辺を丸投げすることも可能です。
参考資料
関連記事
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でプロットしたグラフを表示させる
コメント