Compare commits

...

2 Commits

Author SHA1 Message Date
IluaAir
cd51902313 add login endpoint 2025-09-21 12:08:04 +03:00
IluaAir
8620e9b5a1 add creation refresh token 2025-09-20 21:54:56 +03:00
7 changed files with 45 additions and 8 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,3 +1,4 @@
import secrets
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
import jwt import jwt
@@ -33,6 +34,14 @@ class AuthManager:
algorithm=settings.access_token.algorithm, algorithm=settings.access_token.algorithm,
) )
return encoded_jwt return encoded_jwt
@classmethod
def create_refresh_token(cls) -> str:
# random_bytes = os.urandom(32)
# data = settings.refresh_token.secret_key.encode() + random_bytes
# token_hash = bcrypt.hashpw(data, bcrypt.gensalt(rounds=12)).decode()
token_hash = secrets.token_urlsafe(32)
return token_hash
@classmethod @classmethod
def decode_access_token(cls, token: str) -> dict: def decode_access_token(cls, token: str) -> dict:

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": ...

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}