from typing import List, Optional from authlib.oauth2 import OAuth2Error from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.requests import Request from starlette.responses import JSONResponse, Response from starlette.types import ASGIApp from owslib.auth import ( DEFAULT_CACHE_TIMEOUT, AsyncJWTAuth, async_jwt_auth_from_config, jwt_auth_enabled_for_env, ) from owslib.enums import Environment from owslib.urlpath import is_path_match class JWTAuthenticationMiddleware(BaseHTTPMiddleware): def __init__( self, app: ASGIApp, environment: Environment = Environment.PROD, enabled: Optional[bool] = None, cache_timeout: Optional[int] = DEFAULT_CACHE_TIMEOUT, realm: Optional[str] = None, exclude_paths: Optional[List[str]] = None, auth: Optional[AsyncJWTAuth] = None, ) -> None: super().__init__(app) self.enabled = jwt_auth_enabled_for_env(environment, enabled=enabled) self.exclude_paths = exclude_paths self.auth = auth or async_jwt_auth_from_config( environment=environment, cache_timeout=cache_timeout, realm=realm, ) async def dispatch( self, request: Request, call_next: RequestResponseEndpoint ) -> Response: if self.enabled and not is_path_match( request.url.path, match=self.exclude_paths, ): try: token = await self.auth.authenticate( request.headers.get("authorization") ) except OAuth2Error as exc: return JSONResponse( content={ "code": exc.error or "Authentication error", "message": exc.get_error_description(), "detail": {}, }, status_code=exc.status_code, headers=dict(exc.get_headers()), ) request.scope["token"] = token return await call_next(request)