Adds auth module
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
# ================================================================
|
||||
# services/auth/app/core/security.py (COMPLETE VERSION)
|
||||
# ================================================================
|
||||
"""
|
||||
Security utilities for authentication service
|
||||
"""
|
||||
|
||||
import bcrypt
|
||||
import re
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, Any
|
||||
import redis.asyncio as redis
|
||||
@@ -72,6 +76,11 @@ class SecurityManager:
|
||||
"""Verify JWT token"""
|
||||
return jwt_handler.verify_token(token)
|
||||
|
||||
@staticmethod
|
||||
def hash_token(token: str) -> str:
|
||||
"""Hash token for storage"""
|
||||
return hashlib.sha256(token.encode()).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
async def check_login_attempts(email: str) -> bool:
|
||||
"""Check if user has exceeded login attempts"""
|
||||
@@ -83,71 +92,64 @@ class SecurityManager:
|
||||
return True
|
||||
|
||||
return int(attempts) < settings.MAX_LOGIN_ATTEMPTS
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking login attempts: {e}")
|
||||
return True
|
||||
return True # Allow on error
|
||||
|
||||
@staticmethod
|
||||
async def increment_login_attempts(email: str):
|
||||
"""Increment login attempts counter"""
|
||||
async def increment_login_attempts(email: str) -> None:
|
||||
"""Increment login attempts for email"""
|
||||
try:
|
||||
key = f"login_attempts:{email}"
|
||||
current_attempts = await redis_client.incr(key)
|
||||
|
||||
# Set TTL on first attempt
|
||||
if current_attempts == 1:
|
||||
await redis_client.expire(key, settings.LOCKOUT_DURATION_MINUTES * 60)
|
||||
|
||||
await redis_client.incr(key)
|
||||
await redis_client.expire(key, settings.LOCKOUT_DURATION_MINUTES * 60)
|
||||
except Exception as e:
|
||||
logger.error(f"Error incrementing login attempts: {e}")
|
||||
|
||||
@staticmethod
|
||||
async def clear_login_attempts(email: str):
|
||||
"""Clear login attempts counter"""
|
||||
async def clear_login_attempts(email: str) -> None:
|
||||
"""Clear login attempts for email"""
|
||||
try:
|
||||
key = f"login_attempts:{email}"
|
||||
await redis_client.delete(key)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing login attempts: {e}")
|
||||
|
||||
@staticmethod
|
||||
async def store_refresh_token(user_id: str, refresh_token: str):
|
||||
async def store_refresh_token(user_id: str, token: str) -> None:
|
||||
"""Store refresh token in Redis"""
|
||||
try:
|
||||
key = f"refresh_token:{user_id}"
|
||||
expires_seconds = settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS * 24 * 3600
|
||||
await redis_client.setex(key, expires_seconds, refresh_token)
|
||||
token_hash = SecurityManager.hash_token(token)
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
|
||||
# Store for the duration of the refresh token
|
||||
expire_seconds = settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60
|
||||
await redis_client.setex(key, expire_seconds, "valid")
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing refresh token: {e}")
|
||||
|
||||
@staticmethod
|
||||
async def verify_refresh_token(user_id: str, refresh_token: str) -> bool:
|
||||
"""Verify refresh token"""
|
||||
async def verify_refresh_token(user_id: str, token: str) -> bool:
|
||||
"""Verify refresh token exists in Redis"""
|
||||
try:
|
||||
key = f"refresh_token:{user_id}"
|
||||
stored_token = await redis_client.get(key)
|
||||
|
||||
if stored_token is None:
|
||||
return False
|
||||
|
||||
return stored_token.decode() == refresh_token
|
||||
token_hash = SecurityManager.hash_token(token)
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
|
||||
result = await redis_client.get(key)
|
||||
return result is not None
|
||||
except Exception as e:
|
||||
logger.error(f"Error verifying refresh token: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def revoke_refresh_token(user_id: str):
|
||||
async def revoke_refresh_token(user_id: str, token: str) -> None:
|
||||
"""Revoke refresh token"""
|
||||
try:
|
||||
key = f"refresh_token:{user_id}"
|
||||
token_hash = SecurityManager.hash_token(token)
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
await redis_client.delete(key)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error revoking refresh token: {e}")
|
||||
|
||||
# Global security manager instance
|
||||
security_manager = SecurityManager()
|
||||
# Create singleton instance
|
||||
security_manager = SecurityManager()
|
||||
|
||||
Reference in New Issue
Block a user