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

0
core/__init__.py Normal file
View File

38
core/deps.py Normal file
View File

@@ -0,0 +1,38 @@
from datetime import UTC, datetime
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2AuthorizationCodeBearer
from pydantic import ValidationError
from sqlalchemy.ext.asyncio import AsyncSession
from core.secrets import decode_jwt
from db.models import User
from db.session import get_db
from repositories.users import UserRepository
from schemas.jwt import JWTPayload
oauth_scheme = OAuth2AuthorizationCodeBearer(
authorizationUrl="/auth/login", tokenUrl="/auth/refresh"
)
async def get_current_user(
session: AsyncSession = Depends(get_db), token: str = Depends(oauth_scheme)
) -> User | None:
content = decode_jwt(token)
try:
payload = JWTPayload.model_validate(content)
except ValidationError:
raise HTTPException(status_code=401, detail="Invalid credentials") from None
if payload.exp < datetime.now(UTC).timestamp():
raise HTTPException(status_code=401, detail="Access token expired")
repo = UserRepository(session)
user = await repo.get_user_by_id(int(payload.sub))
if not user:
raise HTTPException(status_code=401, detail="User not found")
return user

51
core/secrets.py Normal file
View File

@@ -0,0 +1,51 @@
import hashlib
import logging
import secrets
from typing import Any
import jwt
from argon2 import PasswordHasher
from argon2.exceptions import InvalidHashError, VerificationError, VerifyMismatchError
from config import cfg
from schemas.dto import KeyPair
from schemas.jwt import JWTPayload
from schemas.providers import ProvidersType
ctx = PasswordHasher()
logger = logging.getLogger(__name__)
def hash_password(plain_password: str) -> ...:
return ctx.hash(plain_password)
def verify_password(hashed_password: str, plain_password: str) -> bool:
try:
ctx.verify(hashed_password, plain_password)
return True
except (VerifyMismatchError, VerificationError, InvalidHashError):
return False
except Exception:
logger.exception("unexpected error while comparing password and hash")
return False
def generate_jwt(payload: dict[str, Any]) -> str:
return jwt.encode(payload, cfg.private_key, "RS256")
def decode_jwt(token: str) -> dict[str, Any]:
return jwt.decode(token, cfg.private_key, "RS256")
def generate_pair(user_id: int, iss: ProvidersType) -> KeyPair:
payload = JWTPayload(sub=user_id, iss=iss)
access_token = generate_jwt(payload.model_dump())
refresh_token = secrets.token_urlsafe(32)
return KeyPair(access_token=access_token, refresh_token=refresh_token)
def hash_refresh_token(token: str):
return hashlib.sha256(token.encode()).hexdigest()