Authentication Differences Between Airflow 2.x and 3.x in MCP Airflow API
The mcp-airflow-api package automatically switches between HTTP Basic Auth for Airflow 2.x (API v1) and JWT Bearer tokens for Airflow 3.x (API v2), with a fallback to Basic Auth when token acquisition fails.
The mcp-airflow-api repository provides a unified Model Context Protocol (MCP) interface for interacting with Apache Airflow's REST API across major versions. Understanding the authentication differences between Airflow 2.x and 3.x is critical for configuring secure connections, as the underlying mechanisms differ significantly in credential handling and session management.
How Authentication Works in Airflow 2.x (API v1)
Airflow 2.x relies exclusively on HTTP Basic Authentication for every API request. The MCP package handles this transparently by reading environment variables and attaching credentials to each call.
Basic Auth Implementation
In src/mcp_airflow_api/functions.py, the airflow_request() function detects API version v1 and creates a BasicAuth object using the username and password from environment variables:
# Lines 64-66 in functions.py
elif api_version == "v1":
from aiohttp import BasicAuth
auth = BasicAuth(username, password)
The credentials are sourced from AIRFLOW_API_USERNAME and AIRFLOW_API_PASSWORD, with the request URL constructed via construct_api_url() to target the /v1/ endpoint path.
How Authentication Works in Airflow 3.x (API v2)
Airflow 3.x introduces JWT (JSON Web Token) Bearer authentication as the primary method, reducing the exposure of raw credentials and eliminating per-request authentication overhead.
JWT Bearer Token Flow
When AIRFLOW_API_VERSION is set to v2, the airflow_request() function first attempts to obtain a cached JWT token via the get_jwt_token() helper (defined in lines 81-124 of functions.py):
# Lines 55-58 in functions.py
if api_version == "v2":
token = await get_jwt_token(username, password, base_url)
if token:
headers["Authorization"] = f"Bearer {token}"
The get_jwt_token() function:
- Checks for a cached token and validates its expiry (~23 hours)
- Calls the
/auth/tokenendpoint using Basic Auth to exchange credentials for a JWT - Caches the
access_tokenand returns it for header injection
Fallback to Basic Auth
If JWT acquisition fails (e.g., the Airflow instance does not support tokens or the auth endpoint is unreachable), the system gracefully falls back to Basic Auth for backward compatibility:
# Lines 61-64 in functions.py (fallback logic)
if not token:
from aiohttp import BasicAuth
auth = BasicAuth(username, password)
This ensures the MCP tools function in mixed environments where some Airflow 3.x deployments may have JWT disabled.
Key Authentication Differences Between Airflow 2.x and 3.x
The authentication models differ in credential transmission, session management, and configuration requirements:
- Credential Exposure: Airflow 2.x sends username and password with every request, while Airflow 3.x exchanges credentials once for a time-bound JWT that expires after approximately 23 hours.
- Header Format: Version 2.x uses
Authorization: Basic <base64_credentials>, whereas version 3.x usesAuthorization: Bearer <jwt_token>when available. - Token Management: The
get_jwt_token()function handles automatic caching and refresh for Airflow 3.x, requiring no manual token management from the user. - Environment Variables: Both versions require
AIRFLOW_API_USERNAME,AIRFLOW_API_PASSWORD, andAIRFLOW_API_BASE_URL, but Airflow 3.x additionally checksAIRFLOW_API_VERSIONto trigger JWT logic. - Endpoint Structure: API v1 paths contain
/v1/, while v2 paths use/v2/, constructed via the sharedconstruct_api_url()utility.
Implementation Details in the Source Code
The authentication abstraction is implemented across several key files in the call518/mcp-airflow-api repository:
src/mcp_airflow_api/functions.py
- Contains the core
airflow_request()function (lines 55-68) that branches based onapi_version - Implements
get_jwt_token()(lines 81-124) for JWT acquisition and caching - Handles the Basic Auth fallback logic for failed token requests
src/mcp_airflow_api/tools/v1_tools.py
- Registers Airflow 2.x specific tools and binds
airflow_request_v1to force API v1 behavior (lines 13-21)
src/mcp_airflow_api/tools/v2_tools.py
- Registers Airflow 3.x specific tools including asset-related endpoints and binds
airflow_request_v2(lines 18-22)
src/mcp_airflow_api/mcp_main.py
- Bootstrap module that loads the appropriate tool set based on the configured API version, determining whether to use Basic Auth or JWT authentication
Summary
- Airflow 2.x (API v1) uses HTTP Basic Auth with credentials sent on every request, implemented in
airflow_request()viaBasicAuthobjects. - Airflow 3.x (API v2) prefers JWT Bearer tokens obtained via
get_jwt_token()and cached for ~23 hours, falling back to Basic Auth when necessary. - The
mcp-airflow-apipackage abstracts these authentication differences through environment variables (AIRFLOW_API_VERSION,AIRFLOW_API_USERNAME,AIRFLOW_API_PASSWORD) and automatic version detection insrc/mcp_airflow_api/functions.py.
Frequently Asked Questions
How do I configure authentication for Airflow 2.x versus Airflow 3.x?
Set the AIRFLOW_API_VERSION environment variable to "v1" for Airflow 2.x or "v2" for Airflow 3.x. Both versions require AIRFLOW_API_USERNAME, AIRFLOW_API_PASSWORD, and AIRFLOW_API_BASE_URL. For Airflow 3.x, the system automatically exchanges these credentials for a JWT token via the /auth/token endpoint, while Airflow 2.x sends the credentials with every request using Basic Auth.
What happens if the JWT token expires during a long-running operation?
The get_jwt_token() function in src/mcp_airflow_api/functions.py caches the JWT token with an expiry timestamp set to approximately 23 hours. If the token expires or is invalid, the function automatically requests a new token from the Airflow server's /auth/token endpoint using the stored credentials. This refresh happens transparently before the next API request is made.
Can I force Basic Auth even when connecting to Airflow 3.x?
Yes, the airflow_request() function includes a fallback mechanism that activates when get_jwt_token() returns None or fails. If the JWT acquisition fails due to network issues, missing endpoints, or disabled token authentication on the Airflow server, the code falls back to creating a BasicAuth object and sending credentials directly. You can also force v1 behavior by setting AIRFLOW_API_VERSION=v1 or using the v1_tools registration path.
Where is the authentication logic implemented in the source code?
The core authentication logic resides in src/mcp_airflow_api/functions.py. Lines 55-68 contain the airflow_request() function that branches between JWT (v2) and Basic Auth (v1) logic. The get_jwt_token() helper (lines 81-124) handles JWT acquisition and caching. Version-specific tool registrations that bind the appropriate request functions are located in src/mcp_airflow_api/tools/v1_tools.py (lines 13-21) and src/mcp_airflow_api/tools/v2_tools.py (lines 18-22).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →