Adds auth module
This commit is contained in:
@@ -1,12 +1,51 @@
|
||||
# ================================================================
|
||||
# services/auth/app/core/database.py (ENHANCED VERSION)
|
||||
# ================================================================
|
||||
"""
|
||||
Database configuration for auth service
|
||||
Database configuration for authentication service
|
||||
"""
|
||||
|
||||
from shared.database.base import DatabaseManager
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.pool import NullPool
|
||||
|
||||
from app.core.config import settings
|
||||
from shared.database.base import Base
|
||||
|
||||
# Initialize database manager
|
||||
database_manager = DatabaseManager(settings.DATABASE_URL)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Alias for convenience
|
||||
get_db = database_manager.get_db
|
||||
# Create async engine
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
poolclass=NullPool,
|
||||
echo=settings.DEBUG,
|
||||
future=True
|
||||
)
|
||||
|
||||
# Create session factory
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
autoflush=False,
|
||||
autocommit=False
|
||||
)
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
"""Database dependency"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
logger.error(f"Database session error: {e}")
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
async def create_tables():
|
||||
"""Create database tables"""
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
logger.info("Database tables created successfully")
|
||||
|
||||
@@ -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