feat: first commit, auth

This commit is contained in:
2026-07-24 11:33:56 +07:00
commit 0d7944fdab
43 changed files with 1303 additions and 0 deletions

75
routes/auth.py Normal file
View File

@@ -0,0 +1,75 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from core.secrets import hash_password, hash_refresh_token, verify_password
from db.session import get_db
from repositories.sessions import SessionsRepository
from repositories.users import UserRepository
from schemas.login import UserLogin, 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")
async def signup(req: UserRegistration, session: AsyncSession = Depends(get_db)):
users_repo = UserRepository(session)
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")
password_hash = hash_password(req.password)
user = await users_repo.create(username=req.username, hashed_password=password_hash)
return JSONResponse(
UserInfo(username=user.username, telegram_id=user.telegram_id).model_dump(),
status_code=201,
)
raise HTTPException(status_code=400, detail="Unsupported provider")
@router.post("/login", response_model=UserLogin)
async def login(req: UserLoginData, session: AsyncSession = Depends(get_db)):
users_repo = UserRepository(session)
sessions_repo = SessionsRepository(session)
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)
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, session: AsyncSession = Depends(get_db)):
sessions_repo = SessionsRepository(session)
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)
return UserTokens(access_token=key_pair.access_token, refresh_token=key_pair.refresh_token)