add login endpoint

This commit is contained in:
IluaAir
2025-09-21 12:08:04 +03:00
parent 8620e9b5a1
commit cd51902313
8 changed files with 41 additions and 21 deletions

View File

@@ -1,10 +1,12 @@
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, Response
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from src.api.dependacies.db_dep import sessionDep from src.api.dependacies.db_dep import sessionDep
from src.api.dependacies.user_dep import ActiveUser
from src.core.settings import settings from src.core.settings import settings
from src.schemas.auth import Token
from src.schemas.users import UserRequestADD from src.schemas.users import UserRequestADD
from src.services.auth import AuthService from src.services.auth import AuthService
@@ -17,12 +19,26 @@ async def registration(session: sessionDep, credential: UserRequestADD):
return auth return auth
@router.post(path="/login") @router.post(path="/login", response_model=Token)
async def login( async def login(
session: sessionDep, session: sessionDep,
credential: Annotated[OAuth2PasswordRequestForm, Depends()], credential: Annotated[OAuth2PasswordRequestForm, Depends()],
response: Response
): ):
access_token = await AuthService(session).login( result = await AuthService(session).login(
credential.username, credential.password credential.username, credential.password
) )
return access_token response.set_cookie(
key="refresh_token",
value=result["refresh_token"],
httponly=True,
samesite='lax',
path=settings.api.v1.auth,
max_age=60 * 60 * 24 * 7
)
return result
@router.post(path="/refresh")
async def refresh(user: ActiveUser, response: Response):
print(response)

View File

@@ -1,7 +1,6 @@
import os import secrets
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
import bcrypt
import jwt import jwt
from passlib.context import CryptContext from passlib.context import CryptContext
@@ -38,9 +37,10 @@ class AuthManager:
@classmethod @classmethod
def create_refresh_token(cls) -> str: def create_refresh_token(cls) -> str:
random_bytes = os.urandom(32) # random_bytes = os.urandom(32)
data = b'settings.refresh_token.secret_key' + random_bytes # data = settings.refresh_token.secret_key.encode() + random_bytes
token_hash = bcrypt.hashpw(data, bcrypt.gensalt(rounds=12)).decode() # token_hash = bcrypt.hashpw(data, bcrypt.gensalt(rounds=12)).decode()
token_hash = secrets.token_urlsafe(32)
return token_hash return token_hash
@classmethod @classmethod

View File

@@ -2,6 +2,7 @@ from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from src.repository.auth import AuthRepo
from src.repository.tasks import TasksRepo from src.repository.tasks import TasksRepo
from src.repository.users import UsersRepo from src.repository.users import UsersRepo
@@ -14,6 +15,7 @@ class DBManager:
self.session: AsyncSession = self.session_factory() self.session: AsyncSession = self.session_factory()
self.user = UsersRepo(self.session) self.user = UsersRepo(self.session)
self.task = TasksRepo(self.session) self.task = TasksRepo(self.session)
self.auth = AuthRepo(self.session)
return self return self
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:

View File

@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Protocol
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
if TYPE_CHECKING: if TYPE_CHECKING:
from src.repository.auth import AuthRepo
from src.repository.tasks import TasksRepo from src.repository.tasks import TasksRepo
from src.repository.users import UsersRepo from src.repository.users import UsersRepo
@@ -15,6 +16,7 @@ class IUOWDB(Protocol):
session: AsyncSession session: AsyncSession
user: 'UsersRepo' user: 'UsersRepo'
task: 'TasksRepo' task: 'TasksRepo'
auth: 'AuthRepo'
async def __aenter__(self) -> "IUOWDB": ... async def __aenter__(self) -> "IUOWDB": ...

View File

@@ -40,18 +40,10 @@ class AccessToken(BaseSettings):
token_type: str = "bearer" # noqa: S105 token_type: str = "bearer" # noqa: S105
class RefreshToken(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", env_prefix="REFRESH_TOKEN_"
)
secret_key: str
class Settings(BaseSettings): class Settings(BaseSettings):
api: ApiPrefix = ApiPrefix() api: ApiPrefix = ApiPrefix()
db: DbSettings = DbSettings() db: DbSettings = DbSettings()
access_token: AccessToken = AccessToken() # type: ignore access_token: AccessToken = AccessToken() # type: ignore
refresh_token: RefreshToken = RefreshToken() # type: ignore
settings = Settings() settings = Settings()

6
src/repository/auth.py Normal file
View File

@@ -0,0 +1,6 @@
from src.models.tokens import RefreshTokensORM
from src.repository.base import BaseRepo
class AuthRepo(BaseRepo):
model: type[RefreshTokensORM] = RefreshTokensORM

View File

@@ -4,6 +4,7 @@ from pydantic import BaseModel, ConfigDict, Field
class Token(BaseModel): class Token(BaseModel):
access_token: str access_token: str
token_type: str token_type: str
model_config = ConfigDict(extra='ignore')
class TokenData(BaseModel): class TokenData(BaseModel):

View File

@@ -2,7 +2,7 @@ from fastapi import HTTPException
from src.core.auth_manager import AuthManager from src.core.auth_manager import AuthManager
from src.core.settings import settings from src.core.settings import settings
from src.schemas.auth import Token, TokenData from src.schemas.auth import TokenData
from src.schemas.users import User, UserAdd, UserRequestADD, UserWithHashedPass from src.schemas.users import User, UserAdd, UserRequestADD, UserWithHashedPass
from src.services.base import BaseService from src.services.base import BaseService
@@ -39,6 +39,7 @@ class AuthService(BaseService):
access_token = AuthManager.create_access_token( access_token = AuthManager.create_access_token(
user_token.model_dump() user_token.model_dump()
) )
return Token( refresh_token = AuthManager.create_refresh_token()
access_token=access_token, token_type=settings.access_token.token_type await self.session.auth.create_one({"token": refresh_token, "user_id": user.id})
) await self.session.commit()
return {"access_token": access_token, "token_type": settings.access_token.token_type, "refresh_token": refresh_token}