сделали авторизацию

This commit is contained in:
Андрей Дувакин 2025-04-27 16:27:58 +05:00
parent 9187518c8b
commit bdb428a1cb
9 changed files with 92 additions and 6 deletions

View File

@ -3,7 +3,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from typing_extensions import Optional from typing_extensions import Optional
from app.domain.models import User from app.domain.models import User, Profile
class UsersRepository: class UsersRepository:
@ -19,7 +19,6 @@ class UsersRepository:
stmt = ( stmt = (
select(User) select(User)
.filter_by(login=login) .filter_by(login=login)
.options(joinedload(User.role))
) )
result = await self.db.execute(stmt) result = await self.db.execute(stmt)
return result.scalars().first() return result.scalars().first()
@ -28,4 +27,4 @@ class UsersRepository:
self.db.add(user) self.db.add(user)
await self.db.commit() await self.db.commit()
await self.db.refresh(user) await self.db.refresh(user)
return user return user

View File

@ -0,0 +1,33 @@
from fastapi import APIRouter, Response, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.session import get_db
from app.domain.entities.auth import AuthEntity
from app.domain.entities.token_entity import TokenEntity
from app.infrastructure.auth_service import AuthService
router = APIRouter()
@router.post(
'/',
response_model=TokenEntity,
summary="User authentication",
description="Logs in the user and outputs the `access_token` in the `cookie'",
)
async def auth_user(
response: Response,
user_data: AuthEntity,
db: AsyncSession = Depends(get_db),
):
auth_service = AuthService(db)
token = await auth_service.authenticate_user(user_data.login, user_data.password)
response.set_cookie(
key="users_access_token",
value=token["access_token"],
httponly=True,
samesite="Lax",
)
return token

View File

@ -1,7 +1,6 @@
from typing import Optional from typing import Optional
from fastapi import APIRouter from fastapi import APIRouter, Depends
from fastapi.params import Depends
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.database.session import get_db from app.database.session import get_db

View File

@ -0,0 +1,5 @@
from app.domain.entities.base_user import BaseUserEntity
class AuthEntity(BaseUserEntity):
pass

View File

@ -0,0 +1,11 @@
from typing import Optional
from pydantic import BaseModel
class TokenEntity(BaseModel):
access_token: str
user_id: int
class Config:
from_attributes = True

View File

@ -0,0 +1,36 @@
import datetime
from typing import Optional
import jwt
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from app.application.users_repository import UsersRepository
from app.settings import get_auth_data
class AuthService:
def __init__(self, db: AsyncSession):
self.users_repository = UsersRepository(db)
async def authenticate_user(self, login: str, password: str) -> Optional[dict]:
user = await self.users_repository.get_by_login(login)
if user and user.check_password(password):
access_token = self.create_access_token({"user_id": user.id})
return {
"access_token": access_token,
"user_id": user.id
}
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid login or password")
@staticmethod
def create_access_token(data: dict) -> str:
to_encode = data.copy()
expire = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=30)
to_encode.update({"exp": expire})
auth_data = get_auth_data()
encode_jwt = jwt.encode(to_encode, auth_data['secret_key'], algorithm=auth_data['algorithm'])
return encode_jwt

View File

@ -2,6 +2,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from app.contollers.register_controller import router as register_router from app.contollers.register_controller import router as register_router
from app.contollers.auth_router import router as auth_router
from app.settings import settings from app.settings import settings
@ -17,6 +18,7 @@ def start_app():
) )
api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register']) api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register'])
api_app.include_router(auth_router, prefix=f'{settings.PREFIX}/auth', tags=['auth'])
return api_app return api_app

View File

@ -5,6 +5,7 @@ class Settings(BaseSettings):
DATABASE_URL: str DATABASE_URL: str
SECRET_KEY: str SECRET_KEY: str
PREFIX: str = '' PREFIX: str = ''
ALGORITHM: str = 'HS256'
class Config: class Config:
env_file = '.env' env_file = '.env'
@ -14,7 +15,7 @@ class Settings(BaseSettings):
settings = Settings() settings = Settings()
def det_auth_data(): def get_auth_data():
return { return {
'secret_key': settings.SECRET_KEY, 'secret_key': settings.SECRET_KEY,
'algorithm': settings.ALGORITHM 'algorithm': settings.ALGORITHM

Binary file not shown.