fix(gotrue): parse expires_in as num in Session.fromJson - #1719
Merged
Conversation
Parse expires_in using (json['expires_in'] as num?)?.toInt() so that Session.fromJson handles double values crossing JS interop (such as BroadcastChannel cross-tab sync) on Wasm without throwing. JwtPayload.fromJson and OAuthClientListResponse.fromJson get the same treatment for their numeric fields. Fixes #1687
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The tag this branch is frozen at no longer builds against the current toolchain and the current canonical capability IDs: - Flutter stable now ships AGP 9, which rejects the example app's old Gradle DSL. Ported the example's Gradle config from main. - dart analyze --fatal-infos now reports use_super_parameters in SupabaseStorageClient. main resolved this as part of the fetch layer refactor, which is too large to backport, so the lint is suppressed locally instead. - The compliance workflow validates against supabase/sdk@main, whose capability IDs keep moving, so a frozen branch can never satisfy them. It now only runs for pull requests that target main.
spydon
force-pushed
the
hotfix/gotrue-wasm-expires-in
branch
from
August 14, 2026 14:27
d51f2a5 to
5b68061
Compare
o-santi
approved these changes
Aug 14, 2026
spydon
added a commit
that referenced
this pull request
Aug 14, 2026
Release for the `gotrue` 2.27.x maintenance line, targeting `release/gotrue-2.27.x`. Ships the Wasm `Session.fromJson` fix merged in #1719. ## Versions | Package | Version | Reason | | --- | --- | --- | | `gotrue` | 2.27.1 to 2.27.2 | The fix itself | | `supabase` | 2.16.0 to 2.16.1 | Dependency was updated | | `supabase_flutter` | 2.17.1 to 2.17.2 | Dependency was updated | The two dependent bumps are required rather than incidental. The published `supabase` 2.16.0 pins `gotrue: 2.27.1` exactly and `supabase_flutter` 2.17.1 pins `supabase: 2.16.0` exactly, so publishing `gotrue` on its own would not reach anyone using the higher level packages. The release commit rewrites those pins to 2.27.2 and 2.16.1 respectively. ## The example integration tests needed #1643 backported This branch is cut at the `gotrue-v2.27.1` tag, which is commit #1642, and #1643 landed immediately after it. That is the commit that made these tests pass in the first place, so the branch sits at the last commit where they were still broken. The suite is gated on `startsWith(github.event.pull_request.title, 'chore(release)')`, so it only runs on release pull requests. That is why #1719 skipped it and why this pull request is the first thing on the branch to trigger it, in the same way #1642 was the first trigger ever. The first run here failed on four platforms at three different lines, matching the causes #1643 describes: - `linux` at line 76, `find.text(createdTitle)` matched the create dialog's own text field while it was still animating away, so the following `ListTile` lookup had nothing to descend from. - `macos` at line 50, the seeded `Book flights` row was below the fold. - `windows` at line 97, the renamed row still matched the old title. `1862f74c` is cherry picked here unchanged. Its parent is exactly this branch's base commit, so it applied without conflict. ## storage_client is deliberately excluded #1719 was squash merged, so its single `fix(gotrue):` commit also touches `packages/storage_client/lib/src/storage_client.dart` and the `supabase_flutter` example's Gradle files. Left alone, `melos version` therefore also proposed `storage_client` 2.8.1. That bump was suppressed with `--ignore storage_client`, because the only change to that package is an `// ignore: use_super_parameters` comment added to keep the pipeline green. Publishing it would ship a no-op version whose changelog entry reads "FIX(gotrue): parse expires_in as num in Session.fromJson", and would consume 2.8.1 on a line where `main` is still at 2.8.0. ## Before publishing `release-tag.yml` only triggers on pushes to `main`, so merging this will not create the tags. They need to be pushed manually, or that workflow needs a `workflow_dispatch` trigger, before `release-publish.yml` can be dispatched against `gotrue-v2.27.2`. ## How this was generated `melos version --yes --ignore="storage_client" --no-git-tag-version`, matching the flags `release-prepare.yml` uses apart from the added filter, which the workflow has no input for. `dart pub get` resolves the workspace and `dart analyze packages/gotrue` is clean at these versions.
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.
What
Hotfix for the
gotrue2.27.x line, backporting the WasmSession.fromJsoncrash fix from #1716.The base is
release/gotrue-2.27.x, a maintenance branch cut at thegotrue-v2.27.1tag. It cannot targetmain, becausemainhas since renamed the package tosupabase_auth(#1697) and renamed the public API (#1712), so a PR againstmainwould read as reverting everything merged since the tag.Resolves #1687 for the 2.x line.
The bug
Session.fromJsoncastjson['expires_in']straight toint?. That map does not always come fromjsonDecode.GoTrueClient._mayStartBroadcastChannelalso feeds it payloads that crossed the JavaScript interop boundary throughdartify()inbroadcast_web.dart, where every JavaScript number arrives as adouble.Under
dart2jsthis was invisible, because Dartintanddoubleshare a JavaScriptNumberat runtime, so3600.0 as int?succeeded. Underdart2wasmthey are distinct runtime types and the cast throws:The
json.decode(json.encode(dataMap))round trip inbroadcast_web.dartdoes not rescue this:3600.0encodes to"3600.0"and decodes back to adouble.Because the throw happened inside the
BroadcastChannelmessage listener, outside the setuptry/catch, the rest of the listener was skipped. No_saveSessionor_removeSessionran, andnotifyAllSubscribersnever fired, so receiving tabs silently failed to synchronize login, logout, and token refresh.The fix
expires_inis now parsed as(json['expires_in'] as num?)?.toInt(), which acceptsint,double, andnull.JwtPayload.fromJson(exp,nbf,iat) andOAuthClientListResponse.fromJson(nextPage,lastPage,total) get the same treatment for their numeric fields.I traced the rest of the reachable surface.
dartify()is called in exactly one place in the repository, and the only types built from that data areSession,User,UserIdentity, andFactor.User.fromJsonhas no numeric fields, its timestamps are ISO 8601 strings, so after this change nothing reachable from the interop boundary casts toint. Everything else in the workspace decodes from a string throughdart:convert, where integer literals stayinton every backend.Pipeline fixes
The tag this branch is frozen at no longer builds against the current toolchain, so the second commit carries three unrelated fixes needed to get a green run. All three were verified to be pre-existing drift rather than fallout from this change, by comparing against #1717, an equivalent change on
mainwhose run passed minutes apart.main(AGP 8.13.1 to 9.1.0, Gradle 8.13 to 9.3.1, Kotlin 2.1.20 to 2.4.0). The example ispublish_to: none, so nothing published changes.dart analyze --fatal-infosnow reportsuse_super_parametersonSupabaseStorageClient.mainresolved this as part of the fetch layer refactor in refactor: share the HTTP request pieces between the fetch layers #1647, which gaveStorageBucketApia stored client field. At this tag the superclass stores nothing, so the local field is still needed and the lint is suppressed instead of backporting that refactor.supabase/sdk@main, whose canonical capability identifiers keep moving, so a branch frozen at an old release can never satisfy them. Itspull_requesttrigger is now scoped to pull requests that targetmain.Release notes
melos versionon this branch proposesgotrue2.27.2, plussupabase2.16.1 andsupabase_flutter2.17.2 as dependency cascades. Those cascades are required, not incidental: the publishedsupabase2.16.0 pinsgotrue: 2.27.1exactly andsupabase_flutter2.17.1 pinssupabase: 2.16.0exactly, so publishinggotruealone would reach nobody using the higher level packages.Note that
release-tag.ymlonly triggers on pushes tomain, so merging the version pull request into this maintenance branch will not create the tags. They need to be pushed manually, or that workflow needs aworkflow_dispatchtrigger, beforerelease-publish.ymlcan run againstgotrue-v2.27.2.Testing
dart pub getresolves the workspace cleanly.dart analyze lib testinpackages/gotrue: no issues.dart analyze --fatal-infos packages/storage_client: no issues.dart test test/src/types/session_test.dart test/src/helper_test.dart: 57 passing, including two new tests covering adoubleexpires_inanddoubleexp,nbf, andiat.dart format: clean.