Skip to content

[v3-2-test] Pin pyjwt>=2.11.0 in FAB provider and stabilise JWT tests under PyJWT 2.12 (#66840)#66885

Merged
potiuk merged 1 commit into
apache:v3-2-testfrom
potiuk:backport-79a7a41-v3-2-test
May 16, 2026
Merged

[v3-2-test] Pin pyjwt>=2.11.0 in FAB provider and stabilise JWT tests under PyJWT 2.12 (#66840)#66885
potiuk merged 1 commit into
apache:v3-2-testfrom
potiuk:backport-79a7a41-v3-2-test

Conversation

@potiuk
Copy link
Copy Markdown
Member

@potiuk potiuk commented May 13, 2026

Summary

Backport of #66840 (merged 2026-05-13, commit `79a7a41`) to `v3-2-test`.

Clean `-x` cherry-pick — no conflicts. See the source PR for full motivation.

Test plan

  • Clean `-x` cherry-pick on top of latest `v3-2-test`
  • Wait for CI on `v3-2-test`

🤖 Generated with Claude Code (Opus 4.7, 1M context)

… 2.12 (apache#66840)

PyJWT 2.12.0 (2026-03-12) tightened type validation: jwt.encode now
rejects iss=None with TypeError: Issuer (iss) must be a string.

Two independent symptoms surfaced:

1. flask_jwt_extended.tokens.py does 'from jwt.types import Options',
   and jwt.types.Options was first added in PyJWT 2.11.0. The providers
   Compat 3.0.6 matrix job resolves pyjwt to 2.10.x because airflow-core
   3.0.6's pyjwt floor is permissive, breaking collection of every
   providers/fab/tests/unit/fab/** test with:

     ImportError: cannot import name 'Options' from 'jwt.types'

   Pin pyjwt>=2.11.0 directly in providers/fab/pyproject.toml so the
   FAB provider keeps installing cleanly regardless of which airflow-core
   release it is paired with. This is the original fix.

2. Once pyjwt resolves to 2.12+, every test path that constructs a JWT
   without setting [api_auth] jwt_issuer fails with
   TypeError: Issuer (iss) must be a string. Current main is robust to
   this (commit a440d1d, 2026-01-31, deletes iss from the claims
   when the configured issuer is falsy), but airflow-core 3.0.6
   (released 2025-08-25) predates that fix. Under the Compat 3.0.6
   matrix this manifested as 41 test failures across edge3, keycloak,
   and FAB.

   This is a test-only issue — in production users either configure
   jwt_issuer themselves or the runtime error surfaces immediately;
   the unique hot path is tests that exercise JWT generation under
   default config.

   Fix by setting AIRFLOW__API_AUTH__JWT_ISSUER to a non-empty default
   in the shared test pytest_plugin, so every JWT-generating test path
   is invariant to which airflow-core version is installed. The four
   TestRevokeToken tests in test_tokens.py construct synthetic tokens
   without an iss claim on purpose and now pass issuer=None to the
   validator explicitly so they remain invariant to that default.

Reproduced in:
- https://github.com/apache/airflow/actions/runs/25760423290/job/75664049129
- https://github.com/apache/airflow/actions/runs/25777763902/job/75715167807

(cherry picked from commit 79a7a41)
@potiuk potiuk merged commit d3bd36c into apache:v3-2-test May 16, 2026
112 checks passed
@potiuk potiuk deleted the backport-79a7a41-v3-2-test branch May 16, 2026 00:00
@vatsrahul1001 vatsrahul1001 added the changelog:skip Changes that should be skipped from the changelog (CI, tests, etc..) label May 18, 2026
@vatsrahul1001 vatsrahul1001 added this to the Airflow 3.2.2 milestone May 18, 2026
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
… 2.12 (#66840) (#66885)

PyJWT 2.12.0 (2026-03-12) tightened type validation: jwt.encode now
rejects iss=None with TypeError: Issuer (iss) must be a string.

Two independent symptoms surfaced:

1. flask_jwt_extended.tokens.py does 'from jwt.types import Options',
   and jwt.types.Options was first added in PyJWT 2.11.0. The providers
   Compat 3.0.6 matrix job resolves pyjwt to 2.10.x because airflow-core
   3.0.6's pyjwt floor is permissive, breaking collection of every
   providers/fab/tests/unit/fab/** test with:

     ImportError: cannot import name 'Options' from 'jwt.types'

   Pin pyjwt>=2.11.0 directly in providers/fab/pyproject.toml so the
   FAB provider keeps installing cleanly regardless of which airflow-core
   release it is paired with. This is the original fix.

2. Once pyjwt resolves to 2.12+, every test path that constructs a JWT
   without setting [api_auth] jwt_issuer fails with
   TypeError: Issuer (iss) must be a string. Current main is robust to
   this (commit a440d1d, 2026-01-31, deletes iss from the claims
   when the configured issuer is falsy), but airflow-core 3.0.6
   (released 2025-08-25) predates that fix. Under the Compat 3.0.6
   matrix this manifested as 41 test failures across edge3, keycloak,
   and FAB.

   This is a test-only issue — in production users either configure
   jwt_issuer themselves or the runtime error surfaces immediately;
   the unique hot path is tests that exercise JWT generation under
   default config.

   Fix by setting AIRFLOW__API_AUTH__JWT_ISSUER to a non-empty default
   in the shared test pytest_plugin, so every JWT-generating test path
   is invariant to which airflow-core version is installed. The four
   TestRevokeToken tests in test_tokens.py construct synthetic tokens
   without an iss claim on purpose and now pass issuer=None to the
   validator explicitly so they remain invariant to that default.

Reproduced in:
- https://github.com/apache/airflow/actions/runs/25760423290/job/75664049129
- https://github.com/apache/airflow/actions/runs/25777763902/job/75715167807

(cherry picked from commit 79a7a41)
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
… 2.12 (#66840) (#66885)

PyJWT 2.12.0 (2026-03-12) tightened type validation: jwt.encode now
rejects iss=None with TypeError: Issuer (iss) must be a string.

Two independent symptoms surfaced:

1. flask_jwt_extended.tokens.py does 'from jwt.types import Options',
   and jwt.types.Options was first added in PyJWT 2.11.0. The providers
   Compat 3.0.6 matrix job resolves pyjwt to 2.10.x because airflow-core
   3.0.6's pyjwt floor is permissive, breaking collection of every
   providers/fab/tests/unit/fab/** test with:

     ImportError: cannot import name 'Options' from 'jwt.types'

   Pin pyjwt>=2.11.0 directly in providers/fab/pyproject.toml so the
   FAB provider keeps installing cleanly regardless of which airflow-core
   release it is paired with. This is the original fix.

2. Once pyjwt resolves to 2.12+, every test path that constructs a JWT
   without setting [api_auth] jwt_issuer fails with
   TypeError: Issuer (iss) must be a string. Current main is robust to
   this (commit a440d1d, 2026-01-31, deletes iss from the claims
   when the configured issuer is falsy), but airflow-core 3.0.6
   (released 2025-08-25) predates that fix. Under the Compat 3.0.6
   matrix this manifested as 41 test failures across edge3, keycloak,
   and FAB.

   This is a test-only issue — in production users either configure
   jwt_issuer themselves or the runtime error surfaces immediately;
   the unique hot path is tests that exercise JWT generation under
   default config.

   Fix by setting AIRFLOW__API_AUTH__JWT_ISSUER to a non-empty default
   in the shared test pytest_plugin, so every JWT-generating test path
   is invariant to which airflow-core version is installed. The four
   TestRevokeToken tests in test_tokens.py construct synthetic tokens
   without an iss claim on purpose and now pass issuer=None to the
   validator explicitly so they remain invariant to that default.

Reproduced in:
- https://github.com/apache/airflow/actions/runs/25760423290/job/75664049129
- https://github.com/apache/airflow/actions/runs/25777763902/job/75715167807

(cherry picked from commit 79a7a41)
vatsrahul1001 pushed a commit that referenced this pull request May 21, 2026
… 2.12 (#66840) (#66885)

PyJWT 2.12.0 (2026-03-12) tightened type validation: jwt.encode now
rejects iss=None with TypeError: Issuer (iss) must be a string.

Two independent symptoms surfaced:

1. flask_jwt_extended.tokens.py does 'from jwt.types import Options',
   and jwt.types.Options was first added in PyJWT 2.11.0. The providers
   Compat 3.0.6 matrix job resolves pyjwt to 2.10.x because airflow-core
   3.0.6's pyjwt floor is permissive, breaking collection of every
   providers/fab/tests/unit/fab/** test with:

     ImportError: cannot import name 'Options' from 'jwt.types'

   Pin pyjwt>=2.11.0 directly in providers/fab/pyproject.toml so the
   FAB provider keeps installing cleanly regardless of which airflow-core
   release it is paired with. This is the original fix.

2. Once pyjwt resolves to 2.12+, every test path that constructs a JWT
   without setting [api_auth] jwt_issuer fails with
   TypeError: Issuer (iss) must be a string. Current main is robust to
   this (commit a440d1d, 2026-01-31, deletes iss from the claims
   when the configured issuer is falsy), but airflow-core 3.0.6
   (released 2025-08-25) predates that fix. Under the Compat 3.0.6
   matrix this manifested as 41 test failures across edge3, keycloak,
   and FAB.

   This is a test-only issue — in production users either configure
   jwt_issuer themselves or the runtime error surfaces immediately;
   the unique hot path is tests that exercise JWT generation under
   default config.

   Fix by setting AIRFLOW__API_AUTH__JWT_ISSUER to a non-empty default
   in the shared test pytest_plugin, so every JWT-generating test path
   is invariant to which airflow-core version is installed. The four
   TestRevokeToken tests in test_tokens.py construct synthetic tokens
   without an iss claim on purpose and now pass issuer=None to the
   validator explicitly so they remain invariant to that default.

Reproduced in:
- https://github.com/apache/airflow/actions/runs/25760423290/job/75664049129
- https://github.com/apache/airflow/actions/runs/25777763902/job/75715167807

(cherry picked from commit 79a7a41)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API area:providers changelog:skip Changes that should be skipped from the changelog (CI, tests, etc..) kind:documentation provider:fab

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants