satupeta-main/app/services/auth_service.py

84 lines
3.2 KiB
Python
Raw Normal View History

2026-01-27 02:11:58 +00:00
from datetime import datetime, timedelta
from typing import Dict, Optional
from fastapi import HTTPException, status
from pytz import timezone
from uuid6 import UUID
from app.core.config import settings
from app.core.security import (
create_access_token,
create_refresh_token,
decode_token,
verify_password,
)
from app.models.user_model import UserModel
from app.repositories.token_repository import TokenRepository
from app.repositories.user_repository import UserRepository
class AuthService:
tz = timezone(settings.TIMEZONE)
def __init__(self, user_repository: UserRepository, token_repository: TokenRepository):
self.user_repository = user_repository
self.token_repository = token_repository
async def authenticate_user(self, username: str, password: str) -> Optional[UserModel]:
"""Autentikasi user dengan username dan password."""
user = await self.user_repository.find_by_username(username)
if not user:
return None
if not verify_password(password, user.password):
return None
return user
async def create_tokens(self, user_id: UUID) -> Dict[str, str]:
"""Buat access dan refresh token."""
access_token = create_access_token(user_id)
refresh_token = create_refresh_token(user_id)
now = datetime.now(timezone(settings.TIMEZONE))
refresh_expires_at = now + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
expire_time = now + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_expires_at = expire_time.timestamp()
refresh_token_data = {
"user_id": user_id,
"token": refresh_token,
"expires_at": refresh_expires_at,
}
await self.token_repository.create(refresh_token_data)
return {"access_token": access_token, "refresh_token": refresh_token, "expires_at": access_expires_at}
async def refresh_token(self, refresh_token: str) -> Dict[str, str]:
"""Refresh access token menggunakan refresh token."""
try:
payload = decode_token(refresh_token)
if payload.get("type") != "refresh":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token type")
user_id = payload.get("sub")
if not user_id:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
token_obj = await self.token_repository.find_valid_token(refresh_token, UUID(user_id))
if not token_obj:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Refresh token is invalid or expired"
)
await self.token_repository.revoke_token(refresh_token)
return await self.create_tokens(UUID(user_id))
except Exception:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=f"Could not validate credentials")
async def logout(self, refresh_token: str) -> bool:
"""Logout user dengan merevoke refresh token."""
return await self.token_repository.revoke_token(refresh_token)