Skip to content

fix(oauth): restore pre-6.45.5 token expiry parsing in Python OAuth authenticators - #1188

Merged
Baz (bazarnov) merged 3 commits into
mainfrom
bazarnov/fix-oauth-raw-expires-in
Oct 2, 2026
Merged

Baz (bazarnov) merged 3 commits into
mainfrom
bazarnov/fix-oauth-raw-expires-in

Conversation

@bazarnov

@bazarnov Baz (bazarnov) commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Since #462 (v6.45.5), set_token_expiry_date stores whatever it receives. Connectors that override refresh_access_token against the older contract return the raw expires_in (e.g. 3600):

  • Oauth2Authenticator / DeclarativeOauth2Authenticator subclasses crash on the next expiry check: TypeError: '>' not supported between instances of 'AirbyteDateTime' and 'int'.
  • SingleUseRefreshTokenOauth2Authenticator subclasses store "3600", which reads back as a 1970 epoch, so every request refreshes and rotates the refresh token.

The same release broke a SingleUseRefreshTokenOauth2Authenticator built with only token_expiry_date_format (#1185): a date expires_in raises ValueError: Invalid expires_in value, and an epoch is read as seconds. v6.45.4 parsed a date whenever a format was set; #637 restored that for the declarative factory only.

Changes

  • The three set_token_expiry_date setters parse a non-datetime value again, as before v6.45.5. SingleUse only converts numeric seconds when neither token_expiry_is_time_of_expiration nor token_expiry_date_format is set. Values that cannot be parsed are stored as before.
  • _parse_token_expiration_date passes a datetime through, for custom setters copied from the pre-6.45.5 CDK.
  • SingleUseRefreshTokenOauth2Authenticator.__init__ sets token_expiry_is_time_of_expiration when a format is given, as the factory does since fix: properly set token_expiry_is_time_of_expiration and mask access token when logging #637. The format check in the SingleUse setter is then redundant and is dropped.

Compatibility

The CDK's own refresh path is unchanged (datetimes are stored as before), so manifest-only, Builder and SDM connectors are not affected. Only three niche shapes behave differently, each contrary to the typed contract or to its own configured format, and unused in the monorepo:

  • a SingleUse override returning an epoch with no flag or format is read as seconds, as in v6.45.4;
  • a subclass reading a raw _token_expiry_date back now gets a parsed datetime;
  • a SingleUse with a format whose provider returns seconds reads them as a date (an epoch), as in v6.45.4 and on the declarative path.

Tests

25 new tests in the existing modules: 8 fail on main (the reported cases), 17 guard shapes that already work on main. Each production change is covered by a mutation.

For #1185, test_given_no_message_repository_get_access_token drops the token_expiry_is_time_of_expiration=bool(expiry_date_format) workaround that #462 added (it hid this regression) and gains an epoch case. 2 of its cases fail on main.

Fixes #824
Fixes #1185

…verrides

Since 6.45.5 refresh_access_token returns the expiry as a datetime and
set_token_expiry_date stores its argument unparsed. Overrides written
earlier still return the raw expires_in: the next expiry check raises a
TypeError, and SingleUseRefreshTokenOauth2Authenticator stores it as a
1970 epoch and refreshes on every request.

- Oauth2Authenticator and DeclarativeOauth2Authenticator
  set_token_expiry_date parse a non-datetime value again, as before
  6.45.5. A value that cannot be parsed is stored as before.
- SingleUseRefreshTokenOauth2Authenticator.set_token_expiry_date reads a
  numeric value as seconds when neither token_expiry_is_time_of_expiration
  nor token_expiry_date_format is set. Other values are stored as before.
- _parse_token_expiration_date passes datetimes through, for custom
  setters that parse the CDK's own result with it.
@bazarnov
Baz (bazarnov) requested a review from a team as a code owner September 28, 2026 15:21
@github-actions

Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

Testing This CDK Version

You can test this version of the CDK using the following:

# Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@bazarnov/fix-oauth-raw-expires-in#egg=airbyte-python-cdk[dev]' --help

# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch bazarnov/fix-oauth-raw-expires-in

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /autofix - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test - Runs connector tests with the updated CDK
  • /prerelease - Triggers a prerelease publish with default arguments
  • /poe build - Regenerate git-committed build artifacts, such as the pydantic models which are generated from the manifest JSON schema in YAML.
  • /poe <command> - Runs any poe command in the CDK environment
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

PyTest Results (Fast)

4 948 tests  +35   4 936 ✅ +35   10m 47s ⏱️ -6s
    1 suites ± 0      12 💤 ± 0 
    1 files   ± 0       0 ❌ ± 0 

Results for commit 312fee9. ± Comparison against base commit f243b0f.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

PyTest Results (Full)

4 951 tests  +35   4 939 ✅ +35   15m 25s ⏱️ -25s
    1 suites ± 0      12 💤 ± 0 
    1 files   ± 0       0 ❌ ± 0 

Results for commit 312fee9. ± Comparison against base commit f243b0f.

♻️ This comment has been updated with latest results.

@bazarnov Baz (bazarnov) changed the title fix(oauth): parse a raw expires_in returned by refresh_access_token overrides fix(oauth): restore pre-6.45.5 token expiry parsing in Python OAuth authenticators Oct 2, 2026
@bazarnov
Baz (bazarnov) merged commit 986e9bb into main Oct 2, 2026
28 of 30 checks passed
@bazarnov
Baz (bazarnov) deleted the bazarnov/fix-oauth-raw-expires-in branch October 2, 2026 11:41
ZaneHyattAB added a commit that referenced this pull request Oct 5, 2026
Resolves the test_requests_native_auth.py conflict with #1188: both sides appended tests
to the same class, so all are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants