Repository navigation
chore: release v0.30.4 - #656
Merged
Merged
Conversation
…refresh token is expired
fix: Update phonenumbers version to cover failing validation edgecases
…nfigured origin (GHSA-3vrp-2rj4-wvw6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…with is_valid_frontend_redirect_uri (GHSA-3vrp-2rj4-wvw6) Backport to 0.30: 0.30 has no pydantic recipe configs or plugins, so is_valid_frontend_redirect_uri is a ThirdPartyConfig attribute passed through init, ThirdPartyRecipe.init/__init__ and validate_and_normalise_user_input (which rejects a value that isn't callable, as the pydantic config does on 0.31). The release note is a CHANGELOG.md entry under [unreleased]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…est path (GHSA-43xx-6crf-23mc) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…verified when verification is required (GHSA-4hf2-33qv-rq88) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… target (GHSA-3vrp-2rj4-wvw6) Resolve `.` and `..` path segments (also when written as %2e) in the redirect target as in RFC 3986 section 5.2.4, for http(s) URLs and for custom-scheme URLs whose path starts with /, and keep an empty ? or #, as URL serialisation in node does. is_valid_frontend_redirect_uri and the Location header both get this form, so HTTPS://App.Example.com/a/../cb becomes https://app.example.com/cb. Document the normalised form, and say in the release note to parse the URL instead of comparing strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…nycode form in the Apple callback (GHSA-3vrp-2rj4-wvw6) Convert a non-ASCII host in the configured origin / website_domain to its IDNA A-label (punycode) form with the stdlib idna codec before comparing it with the redirect target, whose host browsers send in that form, so an app configured with https://bücher.example accepts redirects to https://xn--bcher-kva.example. Redirect targets with a non-ASCII host are still rejected. The debug log for a configured origin that can't be compared now says it is not a valid http or https origin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…e with a 400 in the Apple callback (GHSA-3vrp-2rj4-wvw6) A frontendRedirectURI containing a lone surrogate (e.g. \ud800 in the JSON of the state) can't be encoded into a URL. Reject it while decoding the state, so the callback answers with a 400 instead of a 500, in the path, query and fragment alike, and is_valid_frontend_redirect_uri isn't called for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… targets and name userinfo in the error (GHSA-3vrp-2rj4-wvw6) Document in TypeIsValidFrontendRedirectURIFunction that URLs with a backslash, whitespace or a control character, and http(s) URLs with a username or password or a non-ASCII host, are always rejected without calling is_valid_frontend_redirect_uri, and tell hook authors to parse the URL and compare its parts instead of comparing strings. An http(s) redirect target with a username or password is now rejected with a message saying so, instead of one about its scheme. The status code and which targets are rejected are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ert to another punycode form in the Apple callback (GHSA-3vrp-2rj4-wvw6) Python's idna codec implements IDNA 2003, which maps ß to ss and ς to σ and removes zero width (non-)joiners, while browsers keep them (UTS #46). So a configured origin https://straße.example was compared as strasse.example, a different host than the xn--strae-oqa.example that browsers open. Treat a configured origin whose punycode form doesn't decode back to its host (the conversion changed the host instead of only encoding it) as not comparable, which covers these characters and the other ones IDNA 2003 maps, so the callback answers with a 400 without a custom check. Log at debug level that such an origin has to be configured in its punycode (xn--) form, and correct the docstring and the release note, which said the converted form is the one browsers use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…Apple callback (GHSA-3vrp-2rj4-wvw6) Browsers percent-decode the host of http(s) URLs, so for a target like https://app%2eexample.com/ they open app.example.com, while urllib (and so is_valid_frontend_redirect_uri) sees the host app%2eexample.com. Reject http(s) URLs with a % in the host in get_normalised_origin_tuple, before is_valid_frontend_redirect_uri is called, and add them to the documented list of always rejected targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…h prefix in the Apple callback docstrings (GHSA-3vrp-2rj4-wvw6) Only C0 control characters, DEL and whitespace are rejected, not every control character, so say that. Document that normalise_frontend_redirect_uri adds /. in front of the path of a URL without an authority when the path would otherwise start with //. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ct_uri that isn't a function (GHSA-3vrp-2rj4-wvw6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…n sign in/up APIs (T1) The thirdparty signinup, emailpassword signup/signin and passwordless create code APIs now load the session and settle the tenant first, then resolve the provider and run the form/email/phone validators for that tenant. The session tenant still takes precedence over the path tenant. Backport to 0.30: the test's async client fixture uses pytest_asyncio.fixture, since 0.30 runs pytest-asyncio in strict mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
The GitHub provider's get_user_info now calls the configured validate_access_token (the default checks the token against GitHub's application token API) before fetching user info. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…nverified credentials (T4) recover_account_post now loads the generated options before consuming the token and rejects options generated for a different email with INVALID_OPTIONS_ERROR. It runs is_sign_up_allowed before creating a new webauthn recipe user, and if the recovered recipe user's email was not verified before the recovery, it removes the user's existing credentials and revokes its sessions before marking the email verified and linking. Backport to 0.30: 0.30 has no list or remove credential APIs, and its register credential API loads the session with load_session_in_auth_api_if_needed, so the database check added there covers it. The tests for the list and remove credential APIs are left out, and the release note names only the register credential API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…r api key strictly (T5) An empty or whitespace-only dashboard api_key is now normalised to None, so the dashboard uses dashboard sessions, matching the reported auth mode. validate_api_key now requires the "Bearer " prefix, rejects an empty key and compares keys in constant time. Backport to 0.30: validate_and_normalise_user_input takes api_key and admins as arguments on 0.30 (no pydantic DashboardConfig), so the normalisation and its test use those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… per-request providers (T6) merge_config, merge_providers_from_core_and_static, create_provider and GenericProvider._normalize_input now work on copies of the provider config (including the user info map and clients), so per-tenant overrides and the defaults filled in by built-in providers no longer change the static config passed to the thirdparty recipe. The dashboard third party config GET API also copies the static providers and builds new client objects instead of writing query params into them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…verified device matches (T8) remove_device_post now requires completed MFA whenever the user has a verified device, unless the name exactly matches an unverified device and does not match any verified device under case-insensitive, NFKC normalised comparison. Backport to 0.30: the test imports the 0.30 names of the multifactorauth and totp override configs (OverrideConfig). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
is_fake_email now trims and lowercases the email before checking the fake email domains, matching how emails are compared elsewhere. The emailpassword sign in API also checks the stored email of the matched login method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… all codes (T10) revoke_all_codes(phone_number=...) now sends the phone number to the core under phoneNumber instead of email, so the codes of that phone number are revoked. When both email and phone_number are passed, both are sent and the core rejects the request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
should_allow_access now also applies the admins check when the request is routed to the unlink API, which is a GET request that modifies the user. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… type (M1) validate_access_token_structure now rejects payloads that have an stt value other than 0, and stt is a protected access token payload prop. Backport to 0.30: the new test module sets pytestmark = pytest.mark.asyncio, since 0.30 doesn't run pytest with asyncio_mode = "auto". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…without a verified id token (M2) When a specific hd is configured, the Google Workspaces provider now requires either a verified id token (whose hd is checked by validate_id_token_payload) or a matching hd value in the user info response, and rejects the sign in otherwise. Backport to 0.30: the new test module sets pytestmark = mark.asyncio, since 0.30 doesn't run pytest with asyncio_mode = "auto". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ms (M4) A token whose kid is not in a fresh JWKS cache now only triggers a new fetch from the core if the last fetch was at least 500ms ago; otherwise verification fails straight away with TRY_REFRESH_TOKEN. Fetches are serialised with a separate lock, so the cache read/write lock is no longer held during the HTTP request. Backport to 0.30: get_latest_keys keeps taking the 0.30 SessionConfig, and the new test module sets pytestmark = pytest.mark.asyncio, since 0.30 doesn't run pytest with asyncio_mode = "auto". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…_claims_for_session_handle (M7) Pass the session's tenant when handle-based claim validation refetches a claim value, and drop protected props from the payload update before merging it. Backport to 0.30: the new test module sets pytestmark = mark.asyncio, since 0.30 doesn't run pytest with asyncio_mode = "auto". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…allowed first factors (M9) is_valid_first_factor now only falls back to the static or all available first factors when the tenant's first_factors is None, so a tenant with an empty first_factors list rejects every first factor. Backport to 0.30: FactorIds has no get_all_factors on 0.30, so the test lists the factor ids itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… in the release note (M1)
Tell apps that set a custom access token claim named stt to rename it
before upgrading, and that existing sessions carrying it get a 401 and
can't be refreshed or signed out until they are repaired with
merge_into_access_token_payload(session_handle, {}), which removes the
now-protected claim, or revoked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ck without an id token (M2) With a specific hd configured, a Google Workspaces sign in without an id token is now only accepted if the user info response has the same hd. Sign ins with an id token are checked against its hd claim as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
… in the release note (M1) stt can still be set with merge_into_access_token_payload: it only removes an existing stt claim, and stores one passed in its update. create_new_session and claim validation drop it. Say that instead of saying stt can no longer be set as a custom claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ession's tenant in validate_claims_for_session_handle (M7) Note under Changed that validate_claims_for_session_handle now refetches claim values for the session's own tenant and drops protected claims from the payload update it merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
…ipe.reset (M4) The JWKS cache and its refetch time are module level. Without this, a test (or a test server init) that switches to a new core app keeps the previous app's keys, and a token from the new app that arrives within 500ms of the previous fetch is rejected with try-refresh instead of fetching the new keys. That made unrelated session tests fail at random. Node and golang already reset their JWKS cache on session reset. test_session_verification_of_jwt_with_dynamic_signing_key_mode_works_as_expected re-initialised on a new core app and only found the old token's key because the cache leaked across reset(); it now re-initialises on the same core app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174ThwKR7SF8P4J7E79RigE
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Releases 0.30.4 from the 0.30 line. The release notes are the
## [0.30.4]section of CHANGELOG.md.Merging this starts the release pipeline as a backport: an upload to PyPI, a GitHub release that isn't marked latest, and no merge into master or sync of dev.