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

Reply via email to