This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-3-test by this push:
new 9f843f9e539 [v3-3-test] Document that plugin FastAPI apps are not
authenticated by Airflow (#72655) (#72932)
9f843f9e539 is described below
commit 9f843f9e53923b864785aa6d619e08ce0778f4e8
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 11 12:11:02 2026 +0200
[v3-3-test] Document that plugin FastAPI apps are not authenticated by
Airflow (#72655) (#72932)
* 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.
(cherry picked from commit 6e749f72c2e2689d4f5dfe4ce8a293fa131ef37b)
Generated-by: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01XS3bodTDYYGrPmorhtLsjP
Co-authored-by: Jarek Potiuk <[email protected]>
---
.../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 d8952d74d03..9b600f90bc1 100644
--- a/airflow-core/docs/administration-and-deployment/plugins.rst
+++ b/airflow-core/docs/administration-and-deployment/plugins.rst
@@ -224,6 +224,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 adc69a04f4c..439f9295aea 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1584,6 +1584,7 @@ Stackdriver
stackdriver
stacklevel
stacktrace
+Starlette
starttls
stateful
StatefulSet