diff --git a/backend/app/middleware/auth.py b/backend/app/middleware/auth.py index 77a0e1e..583acb6 100644 --- a/backend/app/middleware/auth.py +++ b/backend/app/middleware/auth.py @@ -1,6 +1,5 @@ import os import httpx -from functools import lru_cache from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from jose import jwt, JWTError @@ -13,22 +12,20 @@ ISSUER = f"https://login.microsoftonline.com/{TENANT_ID}/v2.0" bearer_scheme = HTTPBearer(auto_error=False) - -@lru_cache(maxsize=1) -def _fetch_jwks() -> dict: - """Fetch JWKS from Azure. Cached in process memory; restart to refresh.""" - response = httpx.get(JWKS_URL, timeout=10) - response.raise_for_status() - return response.json() +# Module-level cache — populated once per process, never blocks the event loop +_jwks_cache: dict | None = None -def _get_jwks() -> dict: - try: - return _fetch_jwks() - except Exception: - # Clear cache and retry once on failure - _fetch_jwks.cache_clear() - return _fetch_jwks() +async def _get_jwks() -> dict: + """Fetch JWKS from Azure using async HTTP. Cached in process memory.""" + global _jwks_cache + if _jwks_cache is not None: + return _jwks_cache + async with httpx.AsyncClient(timeout=10) as client: + response = await client.get(JWKS_URL) + response.raise_for_status() + _jwks_cache = response.json() + return _jwks_cache async def get_current_user( @@ -42,12 +39,21 @@ async def get_current_user( token = credentials.credentials try: - jwks = _get_jwks() + jwks = await _get_jwks() header = jwt.get_unverified_header(token) key = next( (k for k in jwks["keys"] if k.get("kid") == header.get("kid")), None, ) + if key is None: + # Key not in cache — fetch fresh JWKS once (keys can rotate) + global _jwks_cache + _jwks_cache = None + jwks = await _get_jwks() + key = next( + (k for k in jwks["keys"] if k.get("kid") == header.get("kid")), + None, + ) if key is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unknown signing key") diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 56eb8cf..bb3ffec 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -183,7 +183,8 @@ function NavBar() { const user = accounts[0]; function handleLogout() { - instance.logoutRedirect({ postLogoutRedirectUri: '/gsb' }); + // Sign out from the app only — does not sign out of the Microsoft account + instance.logoutRedirect({ onRedirectNavigate: () => false }); } return (