LifeRPG_v2.0/modern/backend/auth.py

94 lines
3.0 KiB
Python

import os
import time
from fastapi import APIRouter, HTTPException, Depends, Request
from fastapi.responses import JSONResponse
from passlib.hash import bcrypt
import jwt
from . import models
router = APIRouter()
JWT_SECRET = os.getenv('LIFERPG_JWT_SECRET', 'dev_jwt_secret_change')
JWT_ALGO = 'HS256'
JWT_EXP_SECONDS = 60 * 60 * 24 # 1 day
def create_token(payload: dict) -> str:
now = int(time.time())
payload_out = {**payload, 'iat': now, 'exp': now + JWT_EXP_SECONDS}
return jwt.encode(payload_out, JWT_SECRET, algorithm=JWT_ALGO)
def decode_token(token: str) -> dict:
try:
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO])
except Exception:
return {}
@router.post('/signup')
def signup(payload: dict):
email = payload.get('email')
password = payload.get('password')
if not email or not password:
raise HTTPException(status_code=400, detail='email and password required')
db = models.SessionLocal()
try:
existing = db.query(models.User).filter_by(email=email).first()
if existing:
raise HTTPException(status_code=400, detail='email exists')
user = models.User(email=email, password_hash=bcrypt.hash(password), display_name=payload.get('display_name'))
db.add(user)
db.commit()
db.refresh(user)
token = create_token({'sub': user.id})
resp = JSONResponse({'id': user.id, 'email': user.email})
resp.set_cookie('session', token, httponly=True, secure=False, samesite='lax')
return resp
finally:
db.close()
@router.post('/login')
def login(payload: dict):
email = payload.get('email')
password = payload.get('password')
if not email or not password:
raise HTTPException(status_code=400, detail='email and password required')
db = models.SessionLocal()
try:
user = db.query(models.User).filter_by(email=email).first()
if not user or not user.password_hash or not bcrypt.verify(password, user.password_hash):
raise HTTPException(status_code=401, detail='invalid credentials')
token = create_token({'sub': user.id})
resp = JSONResponse({'id': user.id, 'email': user.email})
resp.set_cookie('session', token, httponly=True, secure=False, samesite='lax')
return resp
finally:
db.close()
@router.post('/logout')
def logout():
resp = JSONResponse({'ok': True})
resp.delete_cookie('session')
return resp
def get_current_user(request: Request):
token = request.cookies.get('session')
if not token:
raise HTTPException(status_code=401, detail='not authenticated')
data = decode_token(token)
uid = data.get('sub')
if not uid:
raise HTTPException(status_code=401, detail='invalid token')
db = models.SessionLocal()
try:
user = db.query(models.User).filter_by(id=uid).first()
if not user:
raise HTTPException(status_code=401, detail='user not found')
return user
finally:
db.close()