This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 88083575d88 Widen revoked_token.jti to store external-issuer token
identifiers (#73562)
88083575d88 is described below
commit 88083575d88f8473bcaf7a9dec9ff56dc2c1459f
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Wed Sep 23 15:28:30 2026 +0200
Widen revoked_token.jti to store external-issuer token identifiers (#73562)
The column stored the JWT jti recorded when a token is revoked, sized at 32
characters to fit Airflow's own uuid4().hex. External identity providers
accepted via [api_auth] trusted_jwks_url mint longer jti claims -- RFC 4122
UUIDs are 36 characters -- which overflow the column and raise a DataError on
databases that enforce length (PostgreSQL, MySQL), so those tokens could not be
recorded. Widening to 255 fits any realistic identifier while staying within
the MySQL primary-key index limit.
---
airflow-core/docs/migrations-ref.rst | 5 +-
.../versions/0135_3_4_0_widen_revoked_token_jti.py | 54 ++++++++++++++++++++++
airflow-core/src/airflow/models/revoked_token.py | 2 +-
airflow-core/src/airflow/utils/db.py | 2 +-
.../tests/unit/models/test_revoked_token.py | 11 +++++
docs/spelling_wordlist.txt | 1 +
6 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/airflow-core/docs/migrations-ref.rst
b/airflow-core/docs/migrations-ref.rst
index b2eec9366d6..0e4ed7a6fae 100644
--- a/airflow-core/docs/migrations-ref.rst
+++ b/airflow-core/docs/migrations-ref.rst
@@ -39,7 +39,10 @@ Here's the list of all the Database Migrations that are
executed via when you ru
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
| Revision ID | Revises ID | Airflow Version | Description
|
+=========================+==================+===================+==============================================================+
-| ``b6a9c2e7d410`` (head) | ``f8c2a1d94e03`` | ``3.4.0`` | Add
draining state to DagModel. |
+| ``5182d0596ee2`` (head) | ``b6a9c2e7d410`` | ``3.4.0`` | Widen
revoked_token.jti to store external-issuer token |
+| | | |
identifiers. |
++-------------------------+------------------+-------------------+--------------------------------------------------------------+
+| ``b6a9c2e7d410`` | ``f8c2a1d94e03`` | ``3.4.0`` | Add
draining state to DagModel. |
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
| ``f8c2a1d94e03`` | ``8d3f1a6b2c47`` | ``3.4.0`` | Add
team_name and bundle_names scope columns to job table. |
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
diff --git
a/airflow-core/src/airflow/migrations/versions/0135_3_4_0_widen_revoked_token_jti.py
b/airflow-core/src/airflow/migrations/versions/0135_3_4_0_widen_revoked_token_jti.py
new file mode 100644
index 00000000000..9a01774bcf2
--- /dev/null
+++
b/airflow-core/src/airflow/migrations/versions/0135_3_4_0_widen_revoked_token_jti.py
@@ -0,0 +1,54 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""
+Widen revoked_token.jti to store external-issuer token identifiers.
+
+Revision ID: 5182d0596ee2
+Revises: b6a9c2e7d410
+Create Date: 2026-09-22 00:00:00.000000
+
+"""
+
+from __future__ import annotations
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "5182d0596ee2"
+down_revision = "b6a9c2e7d410"
+branch_labels = None
+depends_on = None
+airflow_version = "3.4.0"
+
+
+def upgrade():
+ """Widen ``revoked_token.jti`` from 32 to 255 characters."""
+ with op.batch_alter_table("revoked_token") as batch_op:
+ batch_op.alter_column(
+ "jti", existing_type=sa.String(32), type_=sa.String(255),
existing_nullable=False
+ )
+
+
+def downgrade():
+ """Narrow ``revoked_token.jti`` from 255 back to 32 characters."""
+ with op.batch_alter_table("revoked_token") as batch_op:
+ batch_op.alter_column(
+ "jti", existing_type=sa.String(255), type_=sa.String(32),
existing_nullable=False
+ )
diff --git a/airflow-core/src/airflow/models/revoked_token.py
b/airflow-core/src/airflow/models/revoked_token.py
index 200be269e32..03a3f09da8c 100644
--- a/airflow-core/src/airflow/models/revoked_token.py
+++ b/airflow-core/src/airflow/models/revoked_token.py
@@ -44,7 +44,7 @@ class RevokedToken(Base):
# Track last cleanup time to avoid running cleanup on every request
_last_cleanup_time: ClassVar[float] = 0.0
- jti: Mapped[str] = mapped_column(String(32), primary_key=True)
+ jti: Mapped[str] = mapped_column(String(255), primary_key=True)
exp: Mapped[datetime] = mapped_column(UtcDateTime, nullable=False,
index=True)
@classmethod
diff --git a/airflow-core/src/airflow/utils/db.py
b/airflow-core/src/airflow/utils/db.py
index c29af4a971c..23945ebecd4 100644
--- a/airflow-core/src/airflow/utils/db.py
+++ b/airflow-core/src/airflow/utils/db.py
@@ -117,7 +117,7 @@ _REVISION_HEADS_MAP: dict[str, str] = {
"3.1.8": "509b94a1042d",
"3.2.0": "1d6611b6ab7c",
"3.3.0": "d2f4e1b3c5a7",
- "3.4.0": "b6a9c2e7d410",
+ "3.4.0": "5182d0596ee2",
}
# Prefix used to identify tables holding data moved during migration.
diff --git a/airflow-core/tests/unit/models/test_revoked_token.py
b/airflow-core/tests/unit/models/test_revoked_token.py
index 25f904b2ab7..48b8bf6738f 100644
--- a/airflow-core/tests/unit/models/test_revoked_token.py
+++ b/airflow-core/tests/unit/models/test_revoked_token.py
@@ -16,6 +16,7 @@
# under the License.
from __future__ import annotations
+import uuid
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock, patch
@@ -23,6 +24,16 @@ from airflow.models.revoked_token import RevokedToken
class TestRevokedTokenModel:
+ def test_jti_column_fits_external_issuer_identifiers(self):
+ """The ``jti`` column must hold identifiers longer than Airflow's own
32-char ``uuid4().hex``.
+
+ External identity providers (accepted via ``[api_auth]
trusted_jwks_url``) mint ``jti``
+ claims such as 36-char RFC 4122 UUIDs; a 32-char column silently drops
them on databases
+ that enforce length, so revocation never records them.
+ """
+ assert RevokedToken.__table__.c.jti.type.length == 255
+ assert len(str(uuid.uuid4())) <=
RevokedToken.__table__.c.jti.type.length
+
def test_revoke_inserts_row(self):
"""Test that revoke calls session.merge with a RevokedToken
instance."""
mock_session = MagicMock()
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 104a9ecb98c..03876053a19 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -947,6 +947,7 @@ Json
json
jsonl
JsonValue
+jti
juli
Jupyter
jupyter