diff --git a/src/api/v1/auth.py b/src/api/v1/auth.py index a969cca..334cc96 100644 --- a/src/api/v1/auth.py +++ b/src/api/v1/auth.py @@ -1,10 +1,12 @@ from typing import Annotated -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Response from fastapi.security import OAuth2PasswordRequestForm from src.api.dependacies.db_dep import sessionDep +from src.api.dependacies.user_dep import ActiveUser from src.core.settings import settings +from src.schemas.auth import Token from src.schemas.users import UserRequestADD from src.services.auth import AuthService @@ -17,12 +19,26 @@ async def registration(session: sessionDep, credential: UserRequestADD): return auth -@router.post(path="/login") +@router.post(path="/login", response_model=Token) async def login( session: sessionDep, credential: Annotated[OAuth2PasswordRequestForm, Depends()], + response: Response ): - access_token = await AuthService(session).login( + result = await AuthService(session).login( 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) diff --git a/src/core/auth_manager.py b/src/core/auth_manager.py index 4c5d523..2a2dd2a 100644 --- a/src/core/auth_manager.py +++ b/src/core/auth_manager.py @@ -1,7 +1,6 @@ -import os +import secrets from datetime import datetime, timedelta, timezone -import bcrypt import jwt from passlib.context import CryptContext @@ -38,9 +37,10 @@ class AuthManager: @classmethod def create_refresh_token(cls) -> str: - random_bytes = os.urandom(32) - data = b'settings.refresh_token.secret_key' + random_bytes - token_hash = bcrypt.hashpw(data, bcrypt.gensalt(rounds=12)).decode() + # 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 diff --git a/src/core/db_manager.py b/src/core/db_manager.py index ee461a2..5c399ca 100644 --- a/src/core/db_manager.py +++ b/src/core/db_manager.py @@ -2,6 +2,7 @@ from typing import Any from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker +from src.repository.auth import AuthRepo from src.repository.tasks import TasksRepo from src.repository.users import UsersRepo @@ -14,6 +15,7 @@ class DBManager: self.session: AsyncSession = self.session_factory() self.user = UsersRepo(self.session) self.task = TasksRepo(self.session) + self.auth = AuthRepo(self.session) return self async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: diff --git a/src/core/interfaces.py b/src/core/interfaces.py index 0468ffc..9dcdff6 100644 --- a/src/core/interfaces.py +++ b/src/core/interfaces.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Protocol from sqlalchemy.ext.asyncio import AsyncSession if TYPE_CHECKING: + from src.repository.auth import AuthRepo from src.repository.tasks import TasksRepo from src.repository.users import UsersRepo @@ -15,6 +16,7 @@ class IUOWDB(Protocol): session: AsyncSession user: 'UsersRepo' task: 'TasksRepo' + auth: 'AuthRepo' async def __aenter__(self) -> "IUOWDB": ... diff --git a/src/core/settings.py b/src/core/settings.py index 6ae3347..cd41b11 100644 --- a/src/core/settings.py +++ b/src/core/settings.py @@ -40,18 +40,10 @@ class AccessToken(BaseSettings): 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): api: ApiPrefix = ApiPrefix() db: DbSettings = DbSettings() access_token: AccessToken = AccessToken() # type: ignore - refresh_token: RefreshToken = RefreshToken() # type: ignore settings = Settings() diff --git a/src/repository/auth.py b/src/repository/auth.py new file mode 100644 index 0000000..3524cab --- /dev/null +++ b/src/repository/auth.py @@ -0,0 +1,6 @@ +from src.models.tokens import RefreshTokensORM +from src.repository.base import BaseRepo + + +class AuthRepo(BaseRepo): + model: type[RefreshTokensORM] = RefreshTokensORM diff --git a/src/schemas/auth.py b/src/schemas/auth.py index 4f09de9..46f0143 100644 --- a/src/schemas/auth.py +++ b/src/schemas/auth.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, ConfigDict, Field class Token(BaseModel): access_token: str token_type: str + model_config = ConfigDict(extra='ignore') class TokenData(BaseModel): diff --git a/src/services/auth.py b/src/services/auth.py index 9c0b626..3f24ff6 100644 --- a/src/services/auth.py +++ b/src/services/auth.py @@ -2,7 +2,7 @@ from fastapi import HTTPException from src.core.auth_manager import AuthManager 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.services.base import BaseService @@ -39,6 +39,7 @@ class AuthService(BaseService): access_token = AuthManager.create_access_token( user_token.model_dump() ) - return Token( - access_token=access_token, token_type=settings.access_token.token_type - ) + refresh_token = AuthManager.create_refresh_token() + 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} \ No newline at end of file