Fast API get_current_user as a service method

65 Views Asked by At

I have service ( UserService) where i store all functionality connected to user. How can I use service method like async def get_current_user as a dependency, in my router function?

from typing import Annotated

from passlib.context import CryptContext

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

from src.auth.schemas import UserInDTO
from src.auth.models import User
from src.auth.messages import errors
from src.auth.services.token_service import TokenService
from src.auth.repositories.user_repository import UserRepository


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="users/token")


class UserService:
    def __init__(self, repository: UserRepository = Depends()) -> None:
        self.repository = repository

    async def register_user(self, user: UserInDTO) -> User:
        if await self.get_user_by_email(email=user.email):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=errors.USER_EMAIL_ALREADY_EXISTS
            )

        user.hashed_password = self.hash_password(user.hashed_password)

        return await self.repository.create(User(**user.model_dump()))

    async def get_current_user(
            self,
            token: Annotated[str, Depends(oauth2_scheme)],
            token_service: TokenService = Depends(),
    ) -> User:
        token_data = token_service.verify_token(token=token)
        email = token_data.email
        user = await self.get_user_by_email(email=email)

        if not email or not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Could not validate credentials",
                headers={"WWW-Authenticate": "Bearer"},
            )

        return user

    async def authenticate_user(self, email: str, password: str) -> User | HTTPException:
        user = await self.get_user_by_email(email=email)

        if not user or not self.verify_password(plain_password=password, hashed_password=user.hashed_password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=errors.USER_INCORECT_CREDENTIALS,
                headers={"WWW-Authenticate": "Bearer"},
            )
        return user

    async def get_user_by_email(self, email: str) -> User:
        return await self.repository.get(identifier=email)

    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        return pwd_context.verify(plain_password, hashed_password)

    def hash_password(self, plain_password: str) -> str:
        return pwd_context.hash(plain_password)

I try create specific file for dependencies where i put something like this:

def get_current_user(user_service: UserService = Depends()) -> User:
    return user_service.get_current_user

and then in my router function

@router.get("me/")
def test_me(user: Annotated\[UserOutDTO, Depends(get_current_user)\]):
    return user

Result:

{'type': 'model_attributes_type', 'loc': ('response',), 'msg': 'Input should be a valid dictionary or object to extract fields from', 'input': \<bound method UserService.get_current_user of \<src.auth.services.user_ser
vice.UserService object at 0x7f2ddd603ed0\>\>, 'url': 'https://errors.pydantic.dev/2.5/v/model_attributes_type'}
0

There are 0 best solutions below