diff --git a/airflow-core/docs/core-concepts/auth-manager/index.rst b/airflow-core/docs/core-concepts/auth-manager/index.rst index bd13d89ace81a..98bd93602819a 100644 --- a/airflow-core/docs/core-concepts/auth-manager/index.rst +++ b/airflow-core/docs/core-concepts/auth-manager/index.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -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. + + Optional methods recommended to override for optimization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -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: diff --git a/airflow-core/docs/index.rst b/airflow-core/docs/index.rst index 78c33348d3f75..c25a724792bcd 100644 --- a/airflow-core/docs/index.rst +++ b/airflow-core/docs/index.rst @@ -176,7 +176,7 @@ so coding will always be required. Operators and hooks CLI Templates - Stable REST API + Airflow public API Configurations Extra packages diff --git a/airflow-core/docs/security/api.rst b/airflow-core/docs/security/api.rst index 0bae61d73634a..1ed568f69b33e 100644 --- a/airflow-core/docs/security/api.rst +++ b/airflow-core/docs/security/api.rst @@ -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": "" + } + +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 " Enabling CORS ------------- diff --git a/airflow-core/docs/stable-rest-api-ref.rst b/airflow-core/docs/stable-rest-api-ref.rst index 673b88cc35bd3..0996dd69dc955 100644 --- a/airflow-core/docs/stable-rest-api-ref.rst +++ b/airflow-core/docs/stable-rest-api-ref.rst @@ -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 diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml index 97f43ee8d9a9d..cb0b38960deba 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml +++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml @@ -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 diff --git a/airflow-core/src/airflow/api_fastapi/core_api/security.py b/airflow-core/src/airflow/api_fastapi/core_api/security.py index b10f7ac8549fc..0b5f51d2c81ec 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/security.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py @@ -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: diff --git a/airflow-core/tests/unit/api_fastapi/auth/managers/test_base_auth_manager.py b/airflow-core/tests/unit/api_fastapi/auth/managers/test_base_auth_manager.py index 741c4962f3467..bd37351fd63a5 100644 --- a/airflow-core/tests/unit/api_fastapi/auth/managers/test_base_auth_manager.py +++ b/airflow-core/tests/unit/api_fastapi/auth/managers/test_base_auth_manager.py @@ -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 @@ -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" diff --git a/docs/conf.py b/docs/conf.py index 6f14370f05736..a4535154cce96 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -906,7 +906,6 @@ def filter_ignore(record: logging.LogRecord) -> bool: "spec": OPENAPI_FILE.as_posix(), "opts": { "hide-hostname": True, - "no-auto-auth": True, }, }, ]