Flaskでリフレッシュトークンを管理する方法まとめ|初心者でもわかる安全なトークン更新の仕組み
生徒
「先生、Flaskでログインするときにトークンを使うって聞いたんですけど、リフレッシュトークンっていうのもあるんですか?」
先生
「そうですね。Flaskでログイン機能を作るときに使うJWTトークンには、通常のアクセス用トークンとリフレッシュトークンの2種類があります。リフレッシュトークンは、期限切れになったトークンを再発行するために使うんです。」
生徒
「へえ!じゃあ、アクセス用トークンが切れてもログインし直さなくていいんですね?」
先生
「その通りです!今日はFlaskでリフレッシュトークンを安全に管理する方法を、一つひとつ丁寧に見ていきましょう。」
1. そもそもリフレッシュトークンとは?
リフレッシュトークン(Refresh Token)とは、アクセス用トークンが期限切れになったときに、新しいトークンを発行するための特別な鍵のようなものです。ログインし直すことなく、再認証をスムーズに行えるようにする仕組みです。
例えば、あなたがWebアプリにログインしていて、5分でトークンが切れる設定だとしましょう。そのたびに再ログインするのは面倒ですよね。そこでリフレッシュトークンを使えば、裏で新しいトークンを発行してくれるので、ユーザーは快適に使い続けられます。
2. FlaskでJWTとリフレッシュトークンを使う準備
Flaskでリフレッシュトークンを扱うには、Flask-JWT-Extendedという拡張機能を使うのが便利です。インストールはとても簡単です。
pip install Flask-JWT-Extended
このライブラリを使うと、ログイン時にアクセス用トークンとリフレッシュトークンを同時に発行したり、トークンを安全に検証する機能が使えます。
3. Flaskでリフレッシュトークンを発行する方法
次のサンプルコードでは、ログイン時に「アクセス用トークン」と「リフレッシュトークン」を同時に返すようにしています。
from flask import Flask, jsonify, request
from flask_jwt_extended import (
JWTManager, create_access_token, create_refresh_token,
jwt_required, get_jwt_identity
)
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "super-secret-key"
jwt = JWTManager(app)
users = {"admin": "1234", "user": "abcd"}
@app.route("/login", methods=["POST"])
def login():
username = request.json.get("username")
password = request.json.get("password")
if username in users and users[username] == password:
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
return jsonify(access_token=access_token, refresh_token=refresh_token)
return jsonify(msg="認証失敗"), 401
このようにしておくと、ログイン時に2つのトークンが発行されます。
- access_token: 実際にAPIアクセスを許可するための短期間トークン
- refresh_token: 新しいaccess_tokenを発行するための長期間トークン
4. リフレッシュトークンを使って新しいトークンを発行する
次に、期限切れになったトークンを更新するAPIを作ってみましょう。
@app.route("/refresh", methods=["POST"])
@jwt_required(refresh=True)
def refresh():
current_user = get_jwt_identity()
new_token = create_access_token(identity=current_user)
return jsonify(access_token=new_token)
@jwt_required(refresh=True)と書くことで、「リフレッシュトークンが必要なルート」であることを指定できます。これにより、アクセス用トークンではアクセスできず、正しいリフレッシュトークンを持つユーザーだけが新しいトークンを受け取れます。
5. トークンの有効期限を設定する
セキュリティの観点から、トークンには有効期限を設定するのが一般的です。Flaskでは次のように設定できます。
from datetime import timedelta
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(minutes=5)
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=30)
この設定では、アクセス用トークンは5分で切れ、リフレッシュトークンは30日間有効になります。これにより、セキュリティと利便性のバランスを保てます。
6. リフレッシュトークンの安全な管理方法
リフレッシュトークンは長期間有効なため、特に安全に扱う必要があります。以下の方法で管理するのがおすすめです。
- ① データベースに保存する: ユーザーごとのトークンをデータベースに保存し、不正利用を検知したら削除できるようにします。
- ② ブラウザのCookieに安全に保存する:
HttpOnly属性を使うことで、JavaScriptからアクセスできないようにします。 - ③ HTTPS通信を使う: トークンが盗まれないように、常にSSLを有効にしましょう。
from flask import make_response
@app.route("/login_secure", methods=["POST"])
def login_secure():
username = request.json.get("username")
password = request.json.get("password")
if username in users and users[username] == password:
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
response = make_response(jsonify(access_token=access_token))
response.set_cookie("refresh_token", refresh_token, httponly=True, secure=True)
return response
return jsonify(msg="認証失敗"), 401
このようにしておけば、リフレッシュトークンはCookieに安全に保存され、JavaScript経由で盗まれる心配が少なくなります。
7. 不正なトークンを無効化する仕組み
もし誰かにトークンを盗まれてしまった場合、そのトークンを「無効化」できる仕組みを作っておくと安心です。Flask-JWT-Extendedには、ブラックリスト(無効トークン一覧)を管理する仕組みを追加することもできます。
app.config["JWT_BLACKLIST_ENABLED"] = True
app.config["JWT_BLACKLIST_TOKEN_CHECKS"] = ["access", "refresh"]
blacklist = set()
@jwt.token_in_blocklist_loader
def check_if_token_revoked(jwt_header, jwt_payload):
return jwt_payload["jti"] in blacklist
こうしておくことで、盗まれたトークンやログアウト時に無効化したトークンを簡単にブロックできます。
8. 実践で役立つリフレッシュトークンの設計ポイント
最後に、実際の開発でリフレッシュトークンを運用する際のポイントをまとめます。
- ・リフレッシュトークンは短期間ごとに更新して、新しいものと入れ替える
- ・ログアウト時に必ずトークンをブラックリストに登録する
- ・トークンをログファイルに出力しない(漏洩リスクあり)
- ・APIアクセスには常にHTTPSを使用する
これらの対策を行うことで、Flaskアプリケーションでも安全にリフレッシュトークンを活用できます。