feat: Allow using OIDC auth cache instead of session (#5746)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
Hristo Kapanakov
2025-08-15 12:43:29 +03:00
committed by GitHub
parent 1c23d855ae
commit c91d216fe9
4 changed files with 446 additions and 1 deletions

View File

@@ -19,6 +19,8 @@ from mealie.routes._base.routers import UserAPIRouter
from mealie.schema.user import PrivateUser
from mealie.schema.user.auth import CredentialsRequestForm
from .auth_cache import AuthCache
public_router = APIRouter(tags=["Users: Authentication"])
user_router = UserAPIRouter(tags=["Users: Authentication"])
logger = root_logger.get_logger("auth")
@@ -27,7 +29,7 @@ remember_me_duration = timedelta(days=14)
settings = get_app_settings()
if settings.OIDC_READY:
oauth = OAuth()
oauth = OAuth(cache=AuthCache())
scope = None
if settings.OIDC_SCOPES_OVERRIDE:
scope = settings.OIDC_SCOPES_OVERRIDE

View File

@@ -0,0 +1,51 @@
import time
from typing import Any
class AuthCache:
def __init__(self, threshold: int = 500, default_timeout: float = 300):
self.default_timeout = default_timeout
self._cache: dict[str, tuple[float, Any]] = {}
self.clear = self._cache.clear
self._threshold = threshold
def _prune(self):
if len(self._cache) > self._threshold:
now = time.time()
toremove = []
for idx, (key, (expires, _)) in enumerate(self._cache.items()):
if (expires != 0 and expires <= now) or idx % 3 == 0:
toremove.append(key)
for key in toremove:
self._cache.pop(key, None)
def _normalize_timeout(self, timeout: float | None) -> float:
if timeout is None:
timeout = self.default_timeout
if timeout > 0:
timeout = time.time() + timeout
return timeout
async def get(self, key: str) -> Any:
try:
expires, value = self._cache[key]
if expires == 0 or expires > time.time():
return value
except KeyError:
return None
async def set(self, key: str, value: Any, timeout: float | None = None) -> bool:
expires = self._normalize_timeout(timeout)
self._prune()
self._cache[key] = (expires, value)
return True
async def delete(self, key: str) -> bool:
return self._cache.pop(key, None) is not None
async def has(self, key: str) -> bool:
try:
expires, value = self._cache[key]
return expires == 0 or expires > time.time()
except KeyError:
return False