Files
malenia-backend/routes/auth.py

107 lines
3.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from core.secrets import (
estimate_password_strength,
hash_password,
hash_refresh_token,
verify_password,
)
from db.session import UnitOfWork, get_uow
from repositories.sessions import SessionsRepository
from repositories.users import UserRepository
from schemas.login import AuthenticatedUser, UserLoginData, UserTokens
from schemas.providers import ProvidersType
from schemas.registration import UserRegistration
from schemas.user import UserInfo
from services.sessions import refresh_token_rotation
from services.users import authorize_user
router = APIRouter(prefix="/auth")
@router.post("/signup", response_model=AuthenticatedUser)
async def signup(req: UserRegistration, uow: UnitOfWork = Depends(get_uow)) -> AuthenticatedUser:
users_repo = UserRepository(uow)
sessions_repo = SessionsRepository(uow)
if req.provider == "credentials":
if not req.username or not req.password:
raise HTTPException(status_code=400, detail="Username or password is not provided")
user = await users_repo.get_user_by_username(req.username)
if user:
raise HTTPException(status_code=409, detail="User already exists")
if not estimate_password_strength(req.password):
raise HTTPException(422, detail="Password is not secure.")
password_hash = hash_password(req.password)
referal_id = None
if req.referal_code:
referal = await users_repo.get_user_by_ref_code(req.referal_code)
referal_id = referal.id if referal else None
user = await users_repo.create(
username=req.username, hashed_password=password_hash, referal_id=referal_id
)
await uow.commit()
info = UserInfo(
username=user.username,
telegram_id=user.telegram_id,
referal_code=user.referal_code,
bonus_balance=user.balance,
)
user_login = await authorize_user(sessions_repo, user, req.provider)
return AuthenticatedUser(
access_token=user_login.access_token,
refresh_token=user_login.refresh_token,
expires_at=user_login.expires_at,
user=info,
)
raise HTTPException(status_code=400, detail="Unsupported provider")
@router.post("/login", response_model=AuthenticatedUser)
async def login(req: UserLoginData, uow: UnitOfWork = Depends(get_uow)):
users_repo = UserRepository(uow)
sessions_repo = SessionsRepository(uow)
if req.provider == "credentials":
if not req.username or not req.password:
raise HTTPException(status_code=400, detail="Username or password is not provided.")
user = await users_repo.get_user_by_username(req.username)
if not user:
raise HTTPException(status_code=401, detail="User doesn't exist.")
if not verify_password(user.hashed_password, req.password):
raise HTTPException(status_code=401, detail="Invalid password")
data = await authorize_user(sessions_repo, user, req.provider)
await uow.commit()
return data
if req.provider == "telegram":
raise HTTPException(status_code=503, detail="Under development :)")
else:
raise HTTPException(status_code=400, detail="Unknown provider.")
@router.post("/refresh", response_model=UserTokens)
async def refresh(refresh_token: str, iss: ProvidersType, uow: UnitOfWork = Depends(get_uow)):
sessions_repo = SessionsRepository(uow)
token_hash = hash_refresh_token(refresh_token)
token_entry = await sessions_repo.get_session_by_hash(token_hash)
if not token_entry:
raise HTTPException(status_code=401, detail="Refresh token is invalid.")
key_pair = await refresh_token_rotation(sessions_repo, token_entry, iss)
await uow.commit()
return UserTokens(
access_token=key_pair.access_token,
refresh_token=key_pair.refresh_token,
expires_at=key_pair.expires_at,
)