This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 6e749f72c2e Document that plugin FastAPI apps are not authenticated by
Airflow (#72655)
6e749f72c2e is described below
commit 6e749f72c2e2689d4f5dfe4ce8a293fa131ef37b
Author: Jarek Potiuk <[email protected]>
AuthorDate: Fri Sep 11 11:30:50 2026 +0200
Document that plugin FastAPI apps are not authenticated by Airflow (#72655)
* Document that plugin FastAPI apps are not authenticated by Airflow
Airflow authenticates the core API with a router-level dependency. A plugin
app is
attached with app.mount(), and a Starlette mount has its own route table
and inherits
none of the parent's dependencies, so that dependency never reaches a
plugin's routes;
no middleware in the API server authenticates them either.
The consequence is that every route a plugin exposes is reachable by
anonymous callers
unless the plugin authenticates it itself -- and the documented example is
exactly that
shape: a plain route, with no authentication and no note saying one is
needed. A
deployment that follows the docs ships plugin endpoints pre-auth without
being told.
Enforcing authentication in core was the alternative and was rejected: it
breaks plugins
that legitimately serve anonymous callers, and access control for a
plugin's own routes
belongs to the plugin author rather than to core.
So document the gap where it is read. The plugins page now states plainly
that Airflow
does not authenticate plugin apps, shows GetUserDep on a route, shows the
application-level form that keeps a later-added route from silently shipping
unauthenticated, and separates authentication from authorization --
including team
scoping, which a plugin must check for itself.
Documentation only; no behaviour change.
* Add Starlette to the docs spelling wordlist
The plugin authentication warning names Starlette when explaining why a
mounted app inherits none of the parent's dependencies, and the docs
spellcheck has no entry for it.
Generated-by: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01XS3bodTDYYGrPmorhtLsjP
---
.../docs/administration-and-deployment/plugins.rst | 47 ++++++++++++++++++++++
docs/spelling_wordlist.txt | 1 +
2 files changed, 48 insertions(+)
diff --git a/airflow-core/docs/administration-and-deployment/plugins.rst
b/airflow-core/docs/administration-and-deployment/plugins.rst
index a4b699fb163..7784df244a6 100644
--- a/airflow-core/docs/administration-and-deployment/plugins.rst
+++ b/airflow-core/docs/administration-and-deployment/plugins.rst
@@ -229,6 +229,53 @@ definitions in Airflow.
app_with_metadata = {"app": app, "url_prefix": "/some_prefix", "name":
"Name of the App"}
+.. warning::
+
+ **Airflow does not authenticate plugin FastAPI apps. Authenticating them
is the
+ plugin author's responsibility.**
+
+ Airflow authenticates the core API with a router-level dependency. A
plugin app is
+ attached with ``app.mount()``, and a Starlette mount has its own route
table and
+ inherits none of the parent's dependencies, so that dependency never
reaches a
+ plugin's routes. No middleware in the API server authenticates them either.
+
+ Every route a plugin exposes is therefore reachable by **anonymous
callers** unless
+ the plugin authenticates it itself. The minimal ``app`` above is a
structural
+ illustration, not a template to deploy as-is.
+
+ Depend on ``GetUserDep`` to require a caller Airflow has authenticated:
+
+ .. code-block:: python
+
+ from fastapi import FastAPI
+
+ from airflow.api_fastapi.core_api.security import GetUserDep
+
+ app = FastAPI()
+
+
+ @app.get("/dashboard")
+ def dashboard(user: GetUserDep):
+ return {"user": user.get_name()}
+
+ Prefer attaching the dependency once, at the application or router level,
so that a
+ route added later does not silently ship unauthenticated:
+
+ .. code-block:: python
+
+ from fastapi import Depends, FastAPI
+
+ from airflow.api_fastapi.core_api.security import get_user
+
+ app = FastAPI(dependencies=[Depends(get_user)])
+
+ Authentication is not authorization. ``GetUserDep`` establishes *who* is
calling;
+ whether that user may perform a given action remains the plugin's own
decision. This
+ applies to team scoping too — in a multi-team deployment, a plugin that
does not check
+ the caller's team serves every team's users the same data.
+
+.. code-block:: python
+
# Creating a FastAPI middleware that will operates on all the server api
requests.
middleware_with_metadata = {
"middleware": TrustedHostMiddleware,
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 140ca2e93aa..74bdc1864ed 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1632,6 +1632,7 @@ Stackdriver
stackdriver
stacklevel
stacktrace
+Starlette
starttls
stateful
StatefulSet