From 8ef0f234e4c91337ea29451bccd382a043e4af71 Mon Sep 17 00:00:00 2001 From: Aljaz Ceru Date: Thu, 18 Sep 2025 07:54:56 +0200 Subject: [PATCH] tshoot --- backend/app/api/v1/auth.py | 7 ++++ backend/app/core/config.py | 20 +++++++---- backend/app/core/security.py | 28 +++++++++++++++ .../components/providers/auth-provider.tsx | 36 ++++++------------- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/backend/app/api/v1/auth.py b/backend/app/api/v1/auth.py index 1389531..c3523b4 100644 --- a/backend/app/api/v1/auth.py +++ b/backend/app/api/v1/auth.py @@ -182,6 +182,9 @@ async def login( # Create tokens access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + logger.info(f"Creating access token with expiration: {access_token_expires}") + logger.info(f"ACCESS_TOKEN_EXPIRE_MINUTES from settings: {settings.ACCESS_TOKEN_EXPIRE_MINUTES}") + access_token = create_access_token( data={ "sub": str(user.id), @@ -234,6 +237,10 @@ async def refresh_token( # Create new access token access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + logger.info(f"REFRESH: Creating new access token with expiration: {access_token_expires}") + logger.info(f"REFRESH: ACCESS_TOKEN_EXPIRE_MINUTES from settings: {settings.ACCESS_TOKEN_EXPIRE_MINUTES}") + logger.info(f"REFRESH: Current UTC time: {datetime.utcnow().isoformat()}") + access_token = create_access_token( data={ "sub": str(user.id), diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 2c1ae90..15f832d 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -22,22 +22,22 @@ class Settings(BaseSettings): LOG_LLM_PROMPTS: bool = os.getenv("LOG_LLM_PROMPTS", "False").lower() == "true" # Set to True to log prompts and context sent to LLM # Database - DATABASE_URL: str = os.getenv("DATABASE_URL", "postgresql://empire_user:empire_pass@localhost:5432/empire_db") + DATABASE_URL: str = os.getenv("DATABASE_URL") # Redis REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379") # Security - JWT_SECRET: str = os.getenv("JWT_SECRET", "your-super-secret-jwt-key-here") + JWT_SECRET: str = os.getenv("JWT_SECRET") JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256") - ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30")) + ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "1440")) # 24 hours REFRESH_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("REFRESH_TOKEN_EXPIRE_MINUTES", "10080")) # 7 days SESSION_EXPIRE_MINUTES: int = int(os.getenv("SESSION_EXPIRE_MINUTES", "1440")) # 24 hours API_KEY_PREFIX: str = os.getenv("API_KEY_PREFIX", "en_") # Admin user provisioning (used only on first startup) - ADMIN_EMAIL: str = os.getenv("ADMIN_EMAIL", "admin@example.com") - ADMIN_PASSWORD: str = os.getenv("ADMIN_PASSWORD", "admin123") + ADMIN_EMAIL: str = os.getenv("ADMIN_EMAIL") + ADMIN_PASSWORD: str = os.getenv("ADMIN_PASSWORD") # Base URL for deriving CORS origins BASE_URL: str = os.getenv("BASE_URL", "localhost") @@ -141,4 +141,12 @@ class Settings(BaseSettings): # Global settings instance -settings = Settings() \ No newline at end of file +settings = Settings() + +# Log configuration values for debugging +import logging +logger = logging.getLogger(__name__) +logger.info(f"JWT Configuration loaded:") +logger.info(f"ACCESS_TOKEN_EXPIRE_MINUTES: {settings.ACCESS_TOKEN_EXPIRE_MINUTES}") +logger.info(f"REFRESH_TOKEN_EXPIRE_MINUTES: {settings.REFRESH_TOKEN_EXPIRE_MINUTES}") +logger.info(f"JWT_ALGORITHM: {settings.JWT_ALGORITHM}") \ No newline at end of file diff --git a/backend/app/core/security.py b/backend/app/core/security.py index 189ab56..f9d7e6d 100644 --- a/backend/app/core/security.py +++ b/backend/app/core/security.py @@ -51,6 +51,13 @@ def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM) + + # Log token creation details + logger.info(f"Created access token for user {data.get('sub')}") + logger.info(f"Token expires at: {expire.isoformat()} (UTC)") + logger.info(f"Current UTC time: {datetime.utcnow().isoformat()}") + logger.info(f"ACCESS_TOKEN_EXPIRE_MINUTES setting: {settings.ACCESS_TOKEN_EXPIRE_MINUTES}") + return encoded_jwt def create_refresh_token(data: Dict[str, Any]) -> str: @@ -64,10 +71,27 @@ def create_refresh_token(data: Dict[str, Any]) -> str: def verify_token(token: str) -> Dict[str, Any]: """Verify JWT token and return payload""" try: + # Log current time before verification + current_time = datetime.utcnow() + logger.info(f"Verifying token at: {current_time.isoformat()} (UTC)") + + # Decode without verification first to check expiration + try: + unverified_payload = jwt.get_unverified_claims(token) + exp_timestamp = unverified_payload.get('exp') + if exp_timestamp: + exp_datetime = datetime.fromtimestamp(exp_timestamp, tz=None) + logger.info(f"Token expiration time: {exp_datetime.isoformat()} (UTC)") + logger.info(f"Time until expiration: {(exp_datetime - current_time).total_seconds()} seconds") + except Exception as decode_error: + logger.warning(f"Could not decode token for expiration check: {decode_error}") + payload = jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM]) + logger.info(f"Token verified successfully for user {payload.get('sub')}") return payload except JWTError as e: logger.warning(f"Token verification failed: {e}") + logger.warning(f"Current UTC time: {datetime.utcnow().isoformat()}") raise AuthenticationError("Invalid token") async def get_current_user( @@ -76,6 +100,10 @@ async def get_current_user( ) -> Dict[str, Any]: """Get current user from JWT token""" try: + # Log server time for debugging clock sync issues + server_time = datetime.utcnow() + logger.info(f"get_current_user called at: {server_time.isoformat()} (UTC)") + payload = verify_token(credentials.credentials) user_id: str = payload.get("sub") if user_id is None: diff --git a/frontend/src/components/providers/auth-provider.tsx b/frontend/src/components/providers/auth-provider.tsx index 427aef1..81b952c 100644 --- a/frontend/src/components/providers/auth-provider.tsx +++ b/frontend/src/components/providers/auth-provider.tsx @@ -3,6 +3,7 @@ import * as React from "react" import { createContext, useContext, useEffect, useState } from "react" import { apiClient } from "@/lib/api-client" +import { tokenManager } from "@/lib/token-manager" interface User { id: string @@ -39,7 +40,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { useEffect(() => { // Check for existing token on mount - const token = localStorage.getItem("access_token") + const token = tokenManager.getAccessToken() if (token) { // Validate token and get user info validateToken(token) @@ -50,20 +51,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const validateToken = async (token: string) => { try { - // Temporarily set token in localStorage for apiClient to use - const previousToken = localStorage.getItem('token') - localStorage.setItem('token', token) - const userData = await apiClient.get("/api-internal/v1/auth/me") setUser(userData) - - // Restore previous token if different - if (previousToken && previousToken !== token) { - localStorage.setItem('token', previousToken) - } } catch (error) { - localStorage.removeItem("access_token") - localStorage.removeItem("refresh_token") + tokenManager.clearTokens() } finally { setIsLoading(false) } @@ -73,10 +64,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { try { const data = await apiClient.post("/api-internal/v1/auth/login", { username, password }) - // Store tokens - localStorage.setItem("access_token", data.access_token) - localStorage.setItem("refresh_token", data.refresh_token) - localStorage.setItem("token", data.access_token) // Also set token for apiClient + // Store tokens using tokenManager + tokenManager.setTokens(data.access_token, data.refresh_token) // Get user info await validateToken(data.access_token) @@ -89,10 +78,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { try { const data = await apiClient.post("/api-internal/v1/auth/register", { username, email, password }) - // Store tokens - localStorage.setItem("access_token", data.access_token) - localStorage.setItem("refresh_token", data.refresh_token) - localStorage.setItem("token", data.access_token) // Also set token for apiClient + // Store tokens using tokenManager + tokenManager.setTokens(data.access_token, data.refresh_token) // Get user info await validateToken(data.access_token) @@ -102,22 +89,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { } const logout = () => { - localStorage.removeItem("access_token") - localStorage.removeItem("refresh_token") - localStorage.removeItem("token") // Also clear token for apiClient + tokenManager.clearTokens() setUser(null) } const refreshToken = async () => { try { - const refresh_token = localStorage.getItem("refresh_token") + const refresh_token = tokenManager.getRefreshToken() if (!refresh_token) { throw new Error("No refresh token available") } const data = await apiClient.post("/api-internal/v1/auth/refresh", { refresh_token }) - localStorage.setItem("access_token", data.access_token) - localStorage.setItem("token", data.access_token) // Also set token for apiClient + tokenManager.setTokens(data.access_token, refresh_token) return data.access_token } catch (error) {