76 lines
3.1 KiB
Python
76 lines
3.1 KiB
Python
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)
|