apac-ops-bot/backend/app/core/security.py
SamoilenkoVadym 8149a98bd6 Fix test user authentication issues
Fixed email validation and token hashing:
- Changed test user emails from @test.local to @example.com (valid domain)
- Replaced passlib bcrypt for JWT token hashing with SHA-256 (no length limit)
- Improved error handling in SimpleLogin component for validation errors
- Deleted old test users and recreated with valid emails

Credentials:
- Admin: admin@example.com / admin
- User: user@example.com / user

Note: bcrypt still used for password hashing (in auth_service.py),
but SHA-256 for JWT token hashing to avoid 72-byte limit.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 20:09:07 +00:00

134 lines
2.6 KiB
Python

"""
Security utilities
JWT token management and password hashing
"""
import logging
from datetime import datetime, timedelta
from typing import Optional, Dict
import jwt
import hashlib
from app.config import get_settings
logger = logging.getLogger(__name__)
settings = get_settings()
def create_access_token(
data: Dict,
expires_delta: Optional[timedelta] = None
) -> str:
"""
Create JWT access token
Args:
data: Token payload data
expires_delta: Optional expiration time delta
Returns:
Encoded JWT token
"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(hours=1)
to_encode.update({"exp": expire, "iat": datetime.utcnow()})
encoded_jwt = jwt.encode(
to_encode,
settings.SECRET_KEY,
algorithm="HS256"
)
return encoded_jwt
def create_refresh_token(data: Dict) -> str:
"""
Create JWT refresh token (longer expiration)
Args:
data: Token payload data
Returns:
Encoded JWT refresh token
"""
return create_access_token(
data,
expires_delta=timedelta(days=7)
)
def decode_token(token: str) -> Optional[Dict]:
"""
Decode and verify JWT token
Args:
token: JWT token string
Returns:
Decoded token payload or None if invalid
"""
try:
payload = jwt.decode(
token,
settings.SECRET_KEY,
algorithms=["HS256"]
)
return payload
except jwt.ExpiredSignatureError:
logger.warning("Token has expired")
return None
except jwt.InvalidTokenError as e:
logger.warning(f"Invalid token: {e}")
return None
def hash_token(token: str) -> str:
"""
Hash token for secure storage using SHA-256
Args:
token: Token string to hash
Returns:
Hashed token (hex digest)
"""
return hashlib.sha256(token.encode('utf-8')).hexdigest()
def verify_token_hash(token: str, hashed: str) -> bool:
"""
Verify token against hash
Args:
token: Plain token string
hashed: Hashed token
Returns:
True if token matches hash
"""
return hash_token(token) == hashed
def get_token_subject(token: str) -> Optional[str]:
"""
Extract subject (user_id) from token
Args:
token: JWT token string
Returns:
User ID or None
"""
payload = decode_token(token)
if payload:
return payload.get("sub")
return None