Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions airflow-core/docs/core-concepts/auth-manager/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,6 @@ Authentication related BaseAuthManager methods
* ``get_user``: Return the signed-in user.
* ``get_url_login``: Return the URL the user is redirected to for signing in.

JWT token management by auth managers
-------------------------------------
The auth manager is responsible of creating the JWT token and pass it to Airflow UI. The protocol to exchange the JWT
token between the auth manager and Airflow UI is using cookies. The auth manager needs to save the JWT token in a
cookie named ``_token`` before redirecting to the Airflow UI. The Airflow UI will then read the cookie, save it and
delete the cookie.

.. code-block:: python

from airflow.api_fastapi.auth.managers.base_auth_manager import COOKIE_NAME_JWT_TOKEN

response = RedirectResponse(url="/")
response.set_cookie(COOKIE_NAME_JWT_TOKEN, token, secure=True)
return response

.. note::
Do not set the cookie parameter ``httponly`` to ``True``. Airflow UI needs to access the JWT token from the cookie.

Authorization related BaseAuthManager methods
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -147,6 +129,29 @@ These authorization methods are:
* ``is_authorized_view``: Return whether the user is authorized to access a specific view in Airflow. The view is specified through ``access_view`` (e.g. ``AccessView.CLUSTER_ACTIVITY``).
* ``is_authorized_custom_view``: Return whether the user is authorized to access a specific view not defined in Airflow. This view can be provided by the auth manager itself or a plugin defined by the user.

JWT token management by auth managers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The auth manager is responsible for creating the JWT token needed to interact with Airflow public API.
To achieve this, the auth manager **must** provide an endpoint to create this JWT token. This endpoint must be
available at ``POST /auth/token``

The auth manager is also responsible of passing the JWT token to Airflow UI. The protocol to exchange the JWT
token between the auth manager and Airflow UI is using cookies. The auth manager needs to save the JWT token in a
cookie named ``_token`` before redirecting to the Airflow UI. The Airflow UI will then read the cookie, save it and
delete the cookie.

.. code-block:: python

from airflow.api_fastapi.auth.managers.base_auth_manager import COOKIE_NAME_JWT_TOKEN

response = RedirectResponse(url="/")
response.set_cookie(COOKIE_NAME_JWT_TOKEN, token, secure=True)
return response

.. note::
Do not set the cookie parameter ``httponly`` to ``True``. Airflow UI needs to access the JWT token from the cookie.
Comment thread
vincbeck marked this conversation as resolved.


Optional methods recommended to override for optimization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -199,7 +204,7 @@ Such additional endpoints can be used to manage resources such as users, groups,
Endpoints defined by ``get_fastapi_app`` are mounted in ``/auth``.

Next Steps
^^^^^^^^^^
----------

Once you have created a new auth manager class implementing the :class:`~airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` interface, you can configure Airflow to use it by setting the ``core.auth_manager`` configuration value to the module path of your auth manager:

Expand Down
2 changes: 1 addition & 1 deletion airflow-core/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ so coding will always be required.
Operators and hooks <operators-and-hooks-ref>
CLI <cli-and-env-variables-ref>
Templates <templates-ref>
Stable REST API <stable-rest-api-ref>
Airflow public API <stable-rest-api-ref>
Configurations <configurations-ref>
Extra packages <extra-packages-ref>

Expand Down
56 changes: 45 additions & 11 deletions airflow-core/docs/security/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,54 @@
specific language governing permissions and limitations
under the License.

API
===
Public API
==========

API Authentication
------------------
Airflow public API authentication
---------------------------------

The API authentication is handled by the auth manager.
For more information about API authentication, please refer to the auth manager documentation used by your environment.
By default Airflow uses the ``Simple Auth Manager``, if you did not specify any other auth manager.
``Simple Auth Manager`` is a basic auth manager that persisted under Airflow core.
It is not recommended to use it in production and currently aiming for development purposes.
The Airflow public API uses JWT (JSON Web Token) for authenticating API requests.
Each request made to the Airflow API must include a valid JWT token in the ``Authorization`` header to verify the
identity and permissions of the client.

Please install ``apache-airflow-providers-fab`` to use the auth manager that is aimed for production.
For that, please look at :doc:`apache-airflow-providers-fab:auth-manager/api-authentication`.
Generate a JWT token
^^^^^^^^^^^^^^^^^^^^

To interact with the Airflow API, clients must first authenticate and obtain a JWT token.
The token can be generated by making a ``POST`` request to the ``/auth/token`` endpoint, passing the necessary
credentials (e.g., username and password). The ``/auth/token`` endpoint is provided by the auth manager, therefore,
please read the documentation of the auth manager configured in your environment for more details.

Example
*******

Request

.. code-block:: bash

ENDPOINT_URL="http://localhost:8080/"
curl -X POST ${ENDPOINT_URL}/auth/token \
-H "Content-Type: application/json" \
-d '{
"username": "your-username",
"password": "your-password"
}'

Response

.. code-block:: json

{
"jwt_token": "<JWT-TOKEN>"
}

Use the JWT token to call Airflow public API

.. code-block:: bash

ENDPOINT_URL="http://localhost:8080/"
curl -X GET ${ENDPOINT_URL}/api/v2/dags \
-H "Authorization: Bearer <JWT-TOKEN>"

Enabling CORS
-------------
Expand Down
4 changes: 2 additions & 2 deletions airflow-core/docs/stable-rest-api-ref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
specific language governing permissions and limitations
under the License.

REST API Reference
==================
Airflow public API reference
============================

It's a stub file. It will be converted automatically during the build process
to the valid documentation by the Sphinx plugin. See: /docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11782,7 +11782,15 @@ components:
securitySchemes:
OAuth2PasswordBearer:
type: oauth2
description: To authenticate Airflow API requests, clients must include a JWT
(JSON Web Token) in the Authorization header of each request. This token is
used to verify the identity of the client and ensure that they have the appropriate
permissions to access the requested resources. You can use the endpoint ``POST
/auth/token`` in order to generate a JWT token. Upon successful authentication,
the server will issue a JWT token that contains the necessary information
(such as user identity and scope) to authenticate subsequent requests. To
learn more about Airflow public API authentication, please read https://airflow.apache.org/docs/apache-airflow/stable/security/api.html.
flows:
password:
scopes: {}
tokenUrl: token
tokenUrl: /auth/token
12 changes: 11 additions & 1 deletion airflow-core/src/airflow/api_fastapi/core_api/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@

from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager, ResourceMethod

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
auth_description = (
"To authenticate Airflow API requests, clients must include a JWT (JSON Web Token) in "
"the Authorization header of each request. This token is used to verify the identity of "
"the client and ensure that they have the appropriate permissions to access the "
"requested resources. "
"You can use the endpoint ``POST /auth/token`` in order to generate a JWT token. "
"Upon successful authentication, the server will issue a JWT token that contains the necessary "
"information (such as user identity and scope) to authenticate subsequent requests. "
"To learn more about Airflow public API authentication, please read https://airflow.apache.org/docs/apache-airflow/stable/security/api.html."
)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token", description=auth_description)


async def get_user(token_str: Annotated[str, Depends(oauth2_scheme)]) -> BaseUser:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
)
from airflow.api_fastapi.auth.tokens import JWTGenerator, JWTValidator
from airflow.api_fastapi.common.types import MenuItem
from airflow.providers_manager import ProvidersManager
from airflow.utils.module_loading import import_string

if TYPE_CHECKING:
from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
Expand Down Expand Up @@ -366,3 +368,21 @@ def side_effect_func(
session.execute.return_value = dags
result = auth_manager.get_authorized_dag_ids(user=user, session=session)
assert result == expected


def test_auth_managers_have_create_token_endpoint(test_client):
auth_managers = ProvidersManager().auth_managers
auth_managers.append("airflow.api_fastapi.auth.managers.simple.simple_auth_manager.SimpleAuthManager")

for auth_manager_module in auth_managers:
auth_manager_cls = import_string(auth_manager_module)
am = auth_manager_cls()
am.init()

response = test_client.post(
"/auth/token",
json={"username": "", "password": ""},
)
assert (
response.status_code not in [404, 405]
), f"The auth manager {auth_manager_module} does not provide an endpoint to create a JWT token. This endpoint should be POST /auth/token"
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,6 @@ def filter_ignore(record: logging.LogRecord) -> bool:
"spec": OPENAPI_FILE.as_posix(),
"opts": {
"hide-hostname": True,
"no-auto-auth": True,
Comment thread
vincbeck marked this conversation as resolved.
},
},
]
Expand Down