Add AuthStaticFiles for auth on static file serving#1
Conversation
Adds a new AuthStaticFiles class that extends StaticFiles with an `auth` parameter, allowing users to protect static files behind authentication. This addresses a long-standing feature request (issue fastapi#858) where mounted static files bypass FastAPI's dependency injection and serve files without any access control. Closes fastapi#858 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rf docs - Changed default error response from JSON to plain text (browser-friendly) - Added optional `on_error` callback for custom error responses (redirect to login page, HTML error pages, etc.) - Added performance note about keeping auth checks lightweight - Added tests for redirect and HTML custom error responses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughA developer mounts Changes
Sequence DiagramsequenceDiagram
participant Client
participant AuthStatic as AuthStaticFiles
participant AuthFn as auth callback
participant ErrorHandler as on_error callback
participant StarletteStatic as StaticFiles (super)
Client->>AuthStatic: HTTP request (scope, receive, send)
AuthStatic->>AuthFn: await auth(request)
alt Auth raises HTTPException
AuthFn-->>AuthStatic: raises HTTPException
alt on_error provided
AuthStatic->>ErrorHandler: await on_error(request, exc)
ErrorHandler-->>AuthStatic: Response
else no on_error
AuthStatic->>AuthStatic: PlainTextResponse(exc.detail)
end
AuthStatic->>Client: error response
else Auth raises other exception
AuthFn-->>AuthStatic: raises Exception
AuthStatic->>Client: re-raise (500)
else Auth succeeds
AuthFn-->>AuthStatic: returns None
AuthStatic->>StarletteStatic: await super().__call__(scope, receive, send)
StarletteStatic->>Client: serve file or 404
end
Dig Deeper With Commands
Runs only when explicitly triggered. |
| except Exception as exc: | ||
| from fastapi.exceptions import HTTPException | ||
|
|
||
| if isinstance(exc, HTTPException): |
There was a problem hiding this comment.
isinstance only catches fastapi.HTTPException, misses starlette.HTTPException
The except block imports HTTPException from fastapi.exceptions and checks isinstance(exc, HTTPException). Since fastapi.HTTPException is a subclass of starlette.HTTPException, a plain starlette.HTTPException raised by the auth callback won't match — it gets re-raised unhandled instead of going through on_error. This matters because FastAPI's own security modules (APIKeyHeader, OpenIdConnect, etc.) import and raise starlette.exceptions.HTTPException directly. Consider importing from starlette.exceptions instead, or checking for both.
Import from starlette instead: from starlette.exceptions import HTTPException — this will catch both starlette and fastapi HTTPExceptions since fastapi's is a subclass.
Actionable Comments Posted: 1🧹 Nitpick comments (1)Deferred import of HTTPException inside except block on every auth failure - fastapi/staticfiles.py (94, 95)🧾 Coverage Summary✔️ Covered (3 files) |
| html: bool = False, | ||
| check_dir: bool = True, | ||
| follow_symlink: bool = False, | ||
| auth: Callable[[Request], Awaitable[Any]], |
There was a problem hiding this comment.
Sync auth callables will crash at runtime despite no enforcement
The auth parameter is typed as Callable[[Request], Awaitable[Any]] but Python doesn't enforce this at runtime. If a user passes a sync function (returning None or a value), await self.auth(request) at line 93 will raise TypeError: object ... can't be used in 'await' expression. Consider wrapping with asyncio.iscoroutinefunction() check or using run_in_threadpool for sync callables, similar to how FastAPI handles sync dependencies.
Either detect sync callables and wrap them with run_in_threadpool, or add a runtime check in __init__ that raises a clear error if the callable isn't async.
Actionable Comments Posted: 1♻️ Duplicate comments (1)isinstance check won't catch Starlette's HTTPException from auth callbacks - fastapi/staticfiles.py (94, 97)🧹 Nitpick comments (1)on_error type annotation says `Any` but always receives HTTPException - fastapi/staticfiles.py (77)🧾 Coverage Summary✔️ Covered (3 files) |
Mirror of fastapi#15295
Summary by MergeMonkey