JWT Token Authentication
This document describes how JWT (JSON Web Token) authentication works in Apache Airflow for both the public REST API (Core API) and the internal Execution API used by workers.
Overview
Airflow uses JWT tokens as the primary authentication mechanism for its APIs. There are two distinct JWT authentication flows:
REST API (Core API) — used by UI users, CLI tools, and external clients to interact with the Airflow public API.
Execution API — used internally by workers, the Dag File Processor, and the Triggerer to communicate task state and retrieve runtime data (connections, variables, XComs).
Both flows share the same underlying JWT infrastructure (JWTGenerator and JWTValidator
classes in airflow.api_fastapi.auth.tokens) but differ in audience, token lifetime, subject
claims, and scope semantics.
Signing and Cryptography
Airflow supports two mutually exclusive signing modes:
- Symmetric (shared secret)
Uses a pre-shared secret key (
[api_auth] jwt_secret) with the HS512 algorithm. All components that generate or validate tokens must share the same secret. If no secret is configured, Airflow auto-generates a random 16-byte key at startup — but this key is ephemeral and different across processes, which will cause authentication failures in multi-component deployments. Deployment Managers must explicitly configure this value.- Asymmetric (public/private key pair)
Uses a PEM-encoded private key (
[api_auth] jwt_private_key_path) for signing and the corresponding public key for validation. Supported algorithms: RS256 (RSA) and EdDSA (Ed25519). The algorithm is auto-detected from the key type when[api_auth] jwt_algorithmis set toGUESS(the default).Validation can use either:
A JWKS (JSON Web Key Set) endpoint configured via
[api_auth] trusted_jwks_url(local file or remote HTTP/HTTPS URL, polled periodically for updates).The public key derived from the configured private key (automatic fallback when
trusted_jwks_urlis not set).
REST API Authentication Flow
Token acquisition
A client sends a
POSTrequest to/auth/tokenwith credentials (e.g., username and password in JSON body).The auth manager validates the credentials and creates a user object.
The auth manager serializes the user into JWT claims and calls
JWTGenerator.generate().The generated token is returned in the response as
access_token.
For UI-based authentication, the token is stored in a secure, HTTP-only cookie (_token)
with SameSite=Lax.
The CLI uses a separate endpoint (/auth/token/cli) with a different (shorter) expiration
time.
Token structure (REST API)
Claim |
Description |
|---|---|
|
Unique token identifier (UUID4 hex). Used for token revocation. |
|
Issuer (from |
|
Audience (from |
|
User identifier (serialized by the auth manager). |
|
Issued-at timestamp (Unix epoch seconds). |
|
Not-before timestamp (same as |
|
Expiration timestamp ( |
Token validation (REST API)
On each API request, the token is extracted in this order of precedence:
Authorization: Bearer <token>header.OAuth2 query parameter.
_tokencookie.
The JWTValidator verifies the signature, expiry (exp), not-before (nbf),
issued-at (iat), audience, and issuer claims. A configurable leeway
([api_auth] jwt_leeway, default 10 seconds) accounts for clock skew.
Token revocation (REST API only)
Token revocation applies only to REST API and UI tokens — it is not used for Execution API tokens issued to workers.
Revoked tokens are tracked in the revoked_token database table by their jti claim.
On logout or explicit revocation, the token’s jti and exp are inserted into this
table. Expired entries are automatically cleaned up at a cadence of 2× jwt_expiration_time.
Token refresh (REST API)
The JWTRefreshMiddleware runs on UI requests. When the middleware detects that the
current token’s _token cookie is approaching expiry, it calls
auth_manager.refresh_user() to generate a new token and sets it as the updated cookie.
Default timings (REST API)
Setting |
Default |
|---|---|
|
86400 seconds (24 hours) |
|
3600 seconds (1 hour) |
|
10 seconds |
Execution API Authentication Flow
The Execution API is an API used for use by Airflow itself (not third party callers) to report and set task state transitions, send heartbeats, and to retrieve connections, variables, and XComs at task runtime, trigger execution and Dag parsing.
Token generation (Execution API)
The Scheduler generates a JWT for each task instance before dispatching it (via the executor) to a worker. The executor’s
jwt_generatorproperty creates aJWTGeneratorconfigured with the[execution_api]settings.The token’s
sub(subject) claim is set to the task instance UUID.The token is embedded in the workload JSON payload (
BaseWorkloadSchema.tokenfield) that is sent to the worker process.
Token structure (Execution API)
Claim |
Description |
|---|---|
|
Unique token identifier (UUID4 hex). |
|
Issuer (from |
|
Audience (from |
|
Task instance UUID — the identity of the workload. |
|
Token scope: |
|
Issued-at timestamp. |
|
Not-before timestamp. |
|
Expiration timestamp ( |
Token scopes (Execution API)
The Execution API defines two token scopes:
- workload
A restricted scope accepted only on endpoints that explicitly opt in via
Security(require_auth, scopes=["token:workload"]). Used for endpoints that manage task state transitions.- execution
Accepted by all Execution API endpoints. This is the standard scope for worker communication and allows access
Tokens without a scope claim default to "execution" for backwards compatibility.
Token delivery to workers
The token flows through the execution stack as follows:
Scheduler generates the token and embeds it in the workload JSON payload that it passes to Executor.
The workload JSON is passed to the worker process (via the executor-specific mechanism: Celery message, Kubernetes Pod spec, local subprocess arguments, etc.).
The worker’s
execute_workload()function reads the workload JSON and extracts the token.The
supervise()function receives the token and creates anhttpx.Clientinstance withBearerAuth(token)for all Execution API HTTP requests.The token is included in the
Authorization: Bearer <token>header of every request.
Token validation (Execution API)
The JWTBearer security dependency validates the token once per request:
Extracts the token from the
Authorization: Bearerheader.Performs cryptographic signature validation via
JWTValidator.Verifies standard claims (
exp,iat,aud—nbfandissif configured).Defaults the
scopeclaim to"execution"if absent.Creates a
TITokenobject with the task instance ID and claims.Caches the validated token on the ASGI request scope for the duration of the request.
Route-level enforcement is handled by require_auth:
Checks the token’s
scopeagainst the route’sallowed_token_types(precomputed byExecutionAPIRoutefromtoken:*Security scopes at route registration time).Enforces
ti:selfscope — verifies that the token’ssubclaim matches the{task_instance_id}path parameter, preventing a worker from accessing another task’s endpoints.
Token refresh (Execution API)
The JWTReissueMiddleware automatically refreshes valid tokens that are approaching expiry:
After each response, the middleware checks the token’s remaining validity.
If less than 20% of the total validity remains (minimum 30 seconds), the server generates a new token preserving all original claims (including
scopeandsub).The refreshed token is returned in the
Refreshed-API-Tokenresponse header.The client’s
_update_auth()hook detects this header and transparently updates theBearerAuthinstance for subsequent requests.
This mechanism ensures long-running tasks do not lose API access due to token expiry, without requiring the worker to re-authenticate.
No token revocation (Execution API)
Execution API tokens are not subject to revocation. They are short-lived (default 10 minutes)
and automatically refreshed by the JWTReissueMiddleware, so revocation is not part of the
Execution API security model. Once an Execution API token is issued to a worker, it remains
valid until it expires.
Default timings (Execution API)
Setting |
Default |
|---|---|
|
600 seconds (10 minutes) |
|
|
Token refresh threshold |
20% of validity remaining (minimum 30 seconds, i.e., at ~120 seconds before expiry with the default 600-second token lifetime) |
Dag File Processor and Triggerer
The Dag File Processor and Triggerer are internal Airflow components that also
interact with the Execution API, but they do so via an in-process transport
(InProcessExecutionAPI) rather than over the network. This in-process API:
Runs the Execution API application directly within the same process, using an ASGI/WSGI bridge.
Potentially bypasses JWT authentication — the JWT bearer dependency is overridden to always return a synthetic
TITokenwith the"execution"scope, effectively bypassing token validation.Also potentially bypasses per-resource access controls (connection, variable, and XCom access checks are overridden to always allow).
Airflow implements software guards that prevent accidental direct database access from Dag
author code in these components. However, because the child processes that parse Dag files and
execute trigger code run as the same Unix user as their parent processes, these guards do
not protect against intentional access. A deliberately malicious Dag author can potentially
retrieve the parent process’s database credentials (via /proc/<PID>/environ, configuration
files, or secrets manager access) and gain full read/write access to the metadata database and
all Execution API operations — without needing a valid JWT token.
This is in contrast to workers/task execution, where the isolation is implemented ad deployment level - where sensitive configuration of database credentials is not available to Airflow processes because they are not set in their deployment configuration at all, and communicate exclusively through the Execution API.
In the default deployment, a single Dag File Processor instance parses Dag files for all teams and a single Triggerer instance handles all triggers across all teams. This means that Dag author code from different teams executes within the same process, with potentially shared access to the in-process Execution API and the metadata database.
For multi-team deployments that require isolation, Deployment Managers must run separate Dag File Processor and Triggerer instances per team as a deployment-level measure — Airflow does not provide built-in support for per-team DFP or Triggerer instances. Even with separate instances, each retains the same Unix user as the parent process. To prevent credential retrieval, Deployment Managers must implement Unix user-level isolation (running child processes as a different, low-privilege user) or network-level restrictions.
See Airflow Security Model for the full security implications, deployment hardening guidance, and the planned strategic and tactical improvements.
Workload Isolation and Current Limitations
For a detailed discussion of workload isolation protections, current limitations, and planned improvements, see Workload Isolation and Current Limitations.
Configuration Reference
All JWT-related configuration parameters:
Parameter |
Default |
Description |
|---|---|---|
|
Auto-generated if missing |
Symmetric secret key for signing tokens. Must be the same across all components. Mutually exclusive with |
|
None |
Path to PEM-encoded private key ( |
|
|
Signing algorithm. Auto-detected from key type: |
|
Auto ( |
Key ID placed in token header. Ignored for symmetric keys. |
|
None |
Issuer claim ( |
|
None |
Audience claim ( |
|
86400 (24h) |
REST API token lifetime in seconds. |
|
3600 (1h) |
CLI token lifetime in seconds. |
|
10 |
Clock skew tolerance in seconds for token validation. |
|
None |
JWKS endpoint URL or local file path for token validation. Mutually exclusive with |
|
600 (10 min) |
Execution API token lifetime in seconds. |
|
|
Audience claim for Execution API tokens. |
Important
Time synchronization across all Airflow components is critical. Use NTP (e.g., ntpd or
chrony) to keep clocks in sync. Clock skew beyond the configured jwt_leeway will cause
authentication failures.