Adds auth module

This commit is contained in:
Urtzi Alfaro
2025-07-17 21:25:27 +02:00
parent 654d1c2fe8
commit efca9a125a
19 changed files with 1169 additions and 406 deletions

View File

@@ -1,5 +1,8 @@
# ================================================================
# services/auth/app/services/auth_service.py (COMPLETE VERSION)
# ================================================================
"""
Authentication service business logic
Authentication service business logic - Complete implementation
"""
import logging
@@ -59,9 +62,9 @@ class AuthService:
"user_events",
"user.registered",
UserRegisteredEvent(
event_id="",
event_id=str(user.id),
service_name="auth-service",
timestamp= datetime.now(datetime.timezone.utc),
timestamp=datetime.now(datetime.timezone.utc),
data={
"user_id": str(user.id),
"email": user.email,
@@ -111,12 +114,13 @@ class AuthService:
await db.execute(
update(User)
.where(User.id == user.id)
.values(last_login= datetime.now(datetime.timezone.utc))
.values(last_login=datetime.now(datetime.timezone.utc))
)
await db.commit()
# Create tokens
token_data = {
"sub": str(user.id), # Standard JWT claim for subject
"user_id": str(user.id),
"email": user.email,
"tenant_id": str(user.tenant_id) if user.tenant_id else None,
@@ -132,10 +136,10 @@ class AuthService:
# Create session record
session = UserSession(
user_id=user.id,
refresh_token_hash=security_manager.hash_password(refresh_token),
expires_at= datetime.now(datetime.timezone.utc) + timedelta(days=7),
refresh_token_hash=security_manager.hash_token(refresh_token),
ip_address=ip_address,
user_agent=user_agent
user_agent=user_agent,
expires_at=datetime.now(datetime.timezone.utc) + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
)
db.add(session)
@@ -146,9 +150,9 @@ class AuthService:
"user_events",
"user.login",
UserLoginEvent(
event_id="",
event_id=str(session.id),
service_name="auth-service",
timestamp= datetime.now(datetime.timezone.utc),
timestamp=datetime.now(datetime.timezone.utc),
data={
"user_id": str(user.id),
"email": user.email,
@@ -158,38 +162,39 @@ class AuthService:
).__dict__
)
logger.info(f"User logged in: {user.email}")
logger.info(f"User login successful: {user.email}")
return TokenResponse(
access_token=access_token,
refresh_token=refresh_token,
token_type="bearer",
expires_in=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60
)
@staticmethod
async def refresh_token(refresh_token: str, db: AsyncSession) -> TokenResponse:
"""Refresh access token"""
"""Refresh access token using refresh token"""
# Verify refresh token
payload = security_manager.verify_token(refresh_token)
if not payload or payload.get("type") != "refresh":
token_data = security_manager.verify_token(refresh_token)
if not token_data or token_data.get("type") != "refresh":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
)
user_id = payload.get("user_id")
user_id = token_data.get("user_id")
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
detail="Invalid token data"
)
# Verify refresh token is stored
# Check if refresh token exists in Redis
if not await security_manager.verify_refresh_token(user_id, refresh_token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
detail="Refresh token not found or expired"
)
# Get user
@@ -205,80 +210,68 @@ class AuthService:
)
# Create new tokens
token_data = {
new_token_data = {
"sub": str(user.id),
"user_id": str(user.id),
"email": user.email,
"tenant_id": str(user.tenant_id) if user.tenant_id else None,
"role": user.role
}
new_access_token = security_manager.create_access_token(token_data)
new_refresh_token = security_manager.create_refresh_token(token_data)
new_access_token = security_manager.create_access_token(new_token_data)
new_refresh_token = security_manager.create_refresh_token(new_token_data)
# Update stored refresh token
await security_manager.store_refresh_token(str(user.id), new_refresh_token)
# Revoke old refresh token
await security_manager.revoke_refresh_token(user_id, refresh_token)
# Store new refresh token
await security_manager.store_refresh_token(user_id, new_refresh_token)
logger.info(f"Token refreshed for user: {user.email}")
return TokenResponse(
access_token=new_access_token,
refresh_token=new_refresh_token,
token_type="bearer",
expires_in=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60
)
@staticmethod
async def verify_token(token: str, db: AsyncSession) -> Dict[str, Any]:
"""Verify access token"""
async def verify_token(token: str) -> Dict[str, Any]:
"""Verify access token and return user data"""
# Verify token
payload = security_manager.verify_token(token)
if not payload or payload.get("type") != "access":
token_data = security_manager.verify_token(token)
if not token_data or token_data.get("type") != "access":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token"
detail="Invalid access token"
)
user_id = payload.get("user_id")
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token"
)
# Get user
result = await db.execute(
select(User).where(User.id == user_id)
)
user = result.scalar_one_or_none()
if not user or not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or inactive"
)
return {
"user_id": str(user.id),
"email": user.email,
"tenant_id": str(user.tenant_id) if user.tenant_id else None,
"role": user.role,
"full_name": user.full_name,
"is_verified": user.is_verified
}
return token_data
@staticmethod
async def logout_user(user_id: str, db: AsyncSession):
async def logout_user(user_id: str, refresh_token: str, db: AsyncSession) -> bool:
"""Logout user and revoke tokens"""
# Revoke refresh token
await security_manager.revoke_refresh_token(user_id)
# Deactivate user sessions
await db.execute(
update(UserSession)
.where(UserSession.user_id == user_id)
.values(is_active=False)
)
await db.commit()
logger.info(f"User logged out: {user_id}")
try:
# Revoke refresh token
await security_manager.revoke_refresh_token(user_id, refresh_token)
# Deactivate session
await db.execute(
update(UserSession)
.where(
UserSession.user_id == user_id,
UserSession.refresh_token_hash == security_manager.hash_token(refresh_token)
)
.values(is_active=False)
)
await db.commit()
logger.info(f"User logged out: {user_id}")
return True
except Exception as e:
logger.error(f"Error logging out user {user_id}: {e}")
await db.rollback()
return False