This commit is contained in:
2025-09-18 07:54:56 +02:00
parent 994fcdc4bf
commit 8ef0f234e4
4 changed files with 59 additions and 32 deletions

View File

@@ -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),

View File

@@ -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")
@@ -142,3 +142,11 @@ class Settings(BaseSettings):
# Global settings instance
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}")

View File

@@ -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:

View File

@@ -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) {