47 lines
1.3 KiB
Python
47 lines
1.3 KiB
Python
|
|
"""Auth endpoints: login, logout, me."""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from flask import Blueprint, g, jsonify, request
|
||
|
|
|
||
|
|
from backend.app.auth import encode_token, login_required, verify_password
|
||
|
|
from backend.app.models import User
|
||
|
|
from backend.app.serializers import serialize_user
|
||
|
|
|
||
|
|
auth_bp = Blueprint("auth", __name__, url_prefix="/api/auth")
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.post("/login")
|
||
|
|
def login():
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
username = (data.get("username") or "").strip()
|
||
|
|
password = data.get("password") or ""
|
||
|
|
|
||
|
|
generic_error = (jsonify({"error": "Invalid credentials"}), 401)
|
||
|
|
if not username or not password:
|
||
|
|
return generic_error
|
||
|
|
|
||
|
|
user = User.query.filter_by(username=username).first()
|
||
|
|
if user is None or not verify_password(user.password_hash, password):
|
||
|
|
return generic_error
|
||
|
|
|
||
|
|
token = encode_token(user.id, user.role.value)
|
||
|
|
return jsonify(
|
||
|
|
{
|
||
|
|
"access_token": token,
|
||
|
|
"user": {"id": user.id, "username": user.username, "role": user.role.value},
|
||
|
|
}
|
||
|
|
), 200
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.post("/logout")
|
||
|
|
@login_required
|
||
|
|
def logout():
|
||
|
|
# V1: stateless JWT — client discards the token. No server-side blacklist.
|
||
|
|
return jsonify({"status": "ok"}), 200
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.get("/me")
|
||
|
|
@login_required
|
||
|
|
def me():
|
||
|
|
return jsonify(serialize_user(g.current_user)), 200
|