実現したいこと
環境
- python 3.9.6
- flask==2.0.1
- flask-cors==3.0.10
- flask-login==0.5.0
実装例
API要件
- ログインしたらペットが参照できる
- ログインしていなければペットが参照できない
app.py
#!/usr/bin/env python3
from datetime import timedelta
from flask import Flask, jsonify, request
import flask_cors
import flask_login
app = Flask(__name__)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
origins = ["https://AAA.com"]
flask_cors.CORS(
app,
# Cookieとクレデンシャルをドメイン間で送信できる
supports_credentials=True,
# フロントエンドのドメインを指定
origins=origins,
)
app.config.update(
SECRET_KEY="key",
# セッションの有効期限1日
PERMANENT_SESSION_LIFETIME=timedelta(days=1),
# @flask_login.login_requiredのhttp通信を拒否する
SESSION_COOKIE_SECURE=True,
# JavaScriptがCookieへアクセスできなくする
SESSION_COOKIE_HTTPONLY=True,
# クロスドメインのバックエンド⇨クライアントでSet-Cookieを返す
# Secure属性を付けないとNoneにできない
SESSION_COOKIE_SAMESITE="None",
)
class User(flask_login.UserMixin):
def __init__(self, user_id):
self.id = user_id
@login_manager.user_loader
def user_loader(user_id):
return User(user_id)
@app.route("/login", methods=["POST"])
def login():
# formからのCSRF対策でoriginヘッダーをチェックする
if request.headers.get("origin") not in origins:
return jsonify({"message": "Unauthorized"}), 401
# 簡易的なログイン処理
if request.json["user_id"] == "qiita" and request.json["password"] == "1234":
flask_login.login_user(User(request.json["user_id"]))
return jsonify({"message": "Logined"})
else:
# 前のセッションが残らないようにログアウトする
flask_login.logout_user()
return jsonify({"message": "Bad Login"}), 401
@app.route("/pets", methods=["GET"])
# sessionが有効なときにアクセスできる処理
@flask_login.login_required
def pets():
if request.headers.get("origin") not in origins:
return jsonify({"message": "Unauthorized"}), 401
return jsonify({"pets": [{"name": "dog"}]})
@app.route("/logout", methods=["POST"])
@flask_login.login_required
def logout():
if request.headers.get("origin") not in origins:
return jsonify({"message": "Unauthorized"}), 401
flask_login.logout_user()
return jsonify({"message": "Logouted"})
# sessionが有効でないときの処理
@login_manager.unauthorized_handler
def unauthorized_handler():
return jsonify({"message": "Unauthorized"}), 401
参考
フロントエンド側ではクロスドメインでCookieを送信できるようにするため、
HTTPリクエストに"Access-Control-Allow-Credentials: true"ヘッダーを付与する