63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import hashlib
|
||
|
|
from typing import Optional
|
||
|
|
|
||
|
|
import aiosqlite
|
||
|
|
from fastapi import APIRouter, Depends, Request
|
||
|
|
|
||
|
|
from api.deps import error_response, get_db
|
||
|
|
from common import ApiModel
|
||
|
|
from users import repository as users_db
|
||
|
|
from users.models import User
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/auth", tags=["auth-v2"])
|
||
|
|
|
||
|
|
|
||
|
|
class RegisterBody(ApiModel):
|
||
|
|
email: str
|
||
|
|
password: str
|
||
|
|
display_name: str
|
||
|
|
|
||
|
|
|
||
|
|
class LoginBody(ApiModel):
|
||
|
|
email: str
|
||
|
|
password: str
|
||
|
|
|
||
|
|
|
||
|
|
class TokenResponse(ApiModel):
|
||
|
|
access_token: str
|
||
|
|
token_type: str = "bearer"
|
||
|
|
user: User
|
||
|
|
|
||
|
|
|
||
|
|
def _hash_pw(pw: str) -> str:
|
||
|
|
# Placeholder; replace with proper hashing (bcrypt/argon2) later
|
||
|
|
return hashlib.sha256(pw.encode("utf-8")).hexdigest()
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/register", response_model=TokenResponse, operation_id="register")
|
||
|
|
async def register(request: Request, body: RegisterBody, conn: aiosqlite.Connection = Depends(get_db)):
|
||
|
|
existing = await users_db.get_by_email(conn, body.email)
|
||
|
|
if existing:
|
||
|
|
return error_response(request, 400, "Email already registered")
|
||
|
|
uid = await users_db.insert_user(conn, body.email, body.display_name)
|
||
|
|
await users_db.set_local_credentials(conn, uid, _hash_pw(body.password))
|
||
|
|
user = await users_db.get_by_email(conn, body.email)
|
||
|
|
assert user is not None
|
||
|
|
# Token is a simple placeholder containing user id; will be replaced with JWT
|
||
|
|
token = f"user-{user.id}"
|
||
|
|
return TokenResponse(access_token=token, user=user)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/login", response_model=TokenResponse, operation_id="loginV2")
|
||
|
|
async def login(request: Request, body: LoginBody, conn: aiosqlite.Connection = Depends(get_db)):
|
||
|
|
user: Optional[User] = await users_db.get_by_email(conn, body.email)
|
||
|
|
if not user:
|
||
|
|
return error_response(request, 401, "Invalid credentials")
|
||
|
|
stored = await users_db.get_local_password_hash(conn, user.id)
|
||
|
|
if not stored or stored != _hash_pw(body.password):
|
||
|
|
return error_response(request, 401, "Invalid credentials")
|
||
|
|
token = f"user-{user.id}"
|
||
|
|
return TokenResponse(access_token=token, user=user)
|