Skip to content

fix(auth): resolve Firefox content script Promise.then() security errors in locks#2112

Merged
mandarini merged 2 commits intomasterfrom
fix/firefox-issue
Feb 12, 2026
Merged

fix(auth): resolve Firefox content script Promise.then() security errors in locks#2112
mandarini merged 2 commits intomasterfrom
fix/firefox-issue

Conversation

@mandarini
Copy link
Contributor

@mandarini mandarini commented Feb 11, 2026

Description

Fixes Firefox extension content scripts failing when using Supabase auth.

  • Issue: Firefox blocks accessing .then() on cross-context promises from navigator.locks
  • Fix: Replace .then()/.catch() with await/try-catch in locks.ts
  • Impact: Firefox content scripts now work, zero behavior changes elsewhere

Fixes #2108

Changes

  • navigatorLock(): Replace Promise.resolve().then() with sequential awaits
  • processLock(): Replace .catch()/.then() chains with async IIFE + try/catch

Breaking Changes

None. Firefox now works, everything else unchanged.

Summary by CodeRabbit

  • Bug Fixes

    • Improved lock acquisition reliability across contexts by fixing race/await behavior.
    • Navigator lock timeouts now surface a clear timeout error; non-timeout errors are rethrown unchanged.
    • Process-lock timeouts wait for prior operations to complete before resolving to avoid lost work.
  • Tests

    • Added tests validating timeout vs non-timeout lock error behaviors.

@github-actions github-actions bot added the auth-js Related to the auth-js library. label Feb 11, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Updated lock implementations in packages/core/auth-js/src/lib/locks.ts: switched Promise.resolve().then(...) to await-based flows, added try/catch to convert navigator.locks AbortError into NavigatorLockAcquireTimeoutError, and reworked process lock timeout/race handling. Tests for navigatorLock error cases were added.

Changes

Cohort / File(s) Summary
Lock implementation
packages/core/auth-js/src/lib/locks.ts
Replaced Promise.resolve().then(...) with await-based patterns. navigatorLock: surround navigator.locks.request with try/catch, convert AbortError into NavigatorLockAcquireTimeoutError (with timeout message/isAcquireTimeout) and rethrow non-timeout errors. processLock: remove previous Promise.race approach; introduce previousOperationHandled to await prior operation (ignoring its errors), race that against a timeout to emit ProcessLockAcquireTimeoutError, and ensure public per-name lock state wraps currentOperation so timeouts wait for previous operation. Public signatures unchanged.
Tests
packages/core/auth-js/test/lib/locks.test.ts
Added tests validating navigatorLock behavior when navigator.locks.request rejects with AbortError (expect NavigatorLockAcquireTimeoutError with isAcquireTimeout) and when it rejects with other errors (expect original error rethrown).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing Firefox content script errors by removing Promise.then() calls in locks code.
Linked Issues check ✅ Passed The PR directly addresses issue #2108 by replacing Promise.then() chains with await/try-catch patterns in locks.ts, preventing Firefox permission errors on cross-context promises.
Out of Scope Changes check ✅ Passed All changes are scoped to locks.ts and its test file, directly addressing the Firefox navigator.locks permission issue with no extraneous modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/firefox-issue

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/core/auth-js/src/lib/locks.ts (2)

279-284: Misleading comment describes unreachable code path.

The comment "Fall through to run fn() - previous operation finished with error" is misleading because previousOperationHandled catches all its errors internally (lines 236-238), so it never rejects. The only error that can reach this catch block is ProcessLockAcquireTimeoutError from timeoutPromise, which always has isAcquireTimeout = true and will be re-thrown.

Consider removing the dead fall-through path or updating the comment to clarify this is defensive coding.

💡 Suggested simplification
     } catch (e: any) {
       // Clear the timeout on error path as well
       if (timeoutId !== null) {
         clearTimeout(timeoutId)
       }

-      // Re-throw timeout errors, ignore others
-      if (e && e.isAcquireTimeout) {
-        throw e
-      }
-      // Fall through to run fn() - previous operation finished with error
+      // Only timeoutPromise can reject here (previousOperationHandled catches its own errors),
+      // so this is always a timeout error
+      throw e
     }

107-114: Consider clearing the timeout when lock is acquired.

The setTimeout for acquireTimeout is never cleared when the lock is acquired successfully. While functionally harmless (calling abort() on a completed request is a no-op), it causes a misleading debug log message saying "timed out" even when the lock was acquired in time.

💡 Suggested fix to clear timeout
+  let acquireTimeoutId: ReturnType<typeof setTimeout> | null = null
+
   if (acquireTimeout > 0) {
-    setTimeout(() => {
+    acquireTimeoutId = setTimeout(() => {
       abortController.abort()
       if (internals.debug) {
         console.log('@supabase/gotrue-js: navigatorLock acquire timed out', name)
       }
     }, acquireTimeout)
   }

Then clear it when the lock is acquired (around line 140):

if (acquireTimeoutId !== null) {
  clearTimeout(acquireTimeoutId)
}

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 11, 2026

Open in StackBlitz

@supabase/auth-js

npm i https://pkg.pr.new/@supabase/auth-js@2112

@supabase/functions-js

npm i https://pkg.pr.new/@supabase/functions-js@2112

@supabase/postgrest-js

npm i https://pkg.pr.new/@supabase/postgrest-js@2112

@supabase/realtime-js

npm i https://pkg.pr.new/@supabase/realtime-js@2112

@supabase/storage-js

npm i https://pkg.pr.new/@supabase/storage-js@2112

@supabase/supabase-js

npm i https://pkg.pr.new/@supabase/supabase-js@2112

commit: c7c86fa

@coveralls
Copy link

coveralls commented Feb 11, 2026

Coverage Status

coverage: 82.869% (+0.05%) from 82.824%
when pulling c7c86fa on fix/firefox-issue
into b8c75b0 on master.

@mandarini mandarini changed the title fix(auth): firefox then lock issue fix(auth): resolve Firefox content script Promise.then() security errors in locks Feb 11, 2026
@mandarini mandarini marked this pull request as ready for review February 11, 2026 16:41
@mandarini mandarini requested review from a team as code owners February 11, 2026 16:41
@mandarini mandarini self-assigned this Feb 11, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/core/auth-js/src/lib/locks.ts`:
- Around line 229-259: The timeout promise unconditionally logs after
acquireTimeout even if the race already resolved; fix by creating the timeout
with a timer handle (e.g., let timer: NodeJS.Timeout) and only perform
console.warn/reject inside the setTimeout callback, then ensure you
clearTimeout(timer) when the other promise wins; concretely, in the
currentOperation block around previousOperationHandled and acquireTimeout,
create the timeoutPromise with a timer id, run
Promise.race([previousOperationHandled, timeoutPromise]) and after the race
resolves or rejects clearTimeout(timer) (or attach .finally to
previousOperationHandled to clear it) so the warning only fires when the timeout
actually fires; reference symbols: previousOperationHandled, acquireTimeout,
ProcessLockAcquireTimeoutError, currentOperation, name.
- Around line 123-185: The navigator.locks.request call can reject with an
AbortError when abortController.abort() fires (acquireTimeout > 0); wrap the
await globalThis.navigator.locks.request(...) invocation in a try-catch, detect
AbortError (e.g. err.name === 'AbortError' or instanceof DOMException) and
rethrow a NavigatorLockAcquireTimeoutError so callers receive the documented
isAcquireTimeout timeout error; keep existing debug logging around query() and
the fallback path, but ensure the catch converts the abort to
NavigatorLockAcquireTimeoutError while rethrowing other errors unchanged
(reference symbols: navigator.locks.request, abortController, AbortError,
NavigatorLockAcquireTimeoutError, fn, internals.debug).
🧹 Nitpick comments (1)
packages/core/auth-js/src/lib/locks.ts (1)

96-289: Please add/confirm targeted tests for this Firefox regression.
Given this is a cross-library bug fix, it would be great to add a unit test in auth-js for the lock helpers plus an integration test in supabase-js that simulates the content-script context to ensure no .then() access occurs. I can help draft tests if useful. Based on learnings: “For cross-library bug fixes, fix root cause in source library, add unit tests there, add integration tests in supabase-js, verify both test suites, and commit with description of impact.”

@mandarini mandarini merged commit e0d5082 into master Feb 12, 2026
17 checks passed
@mandarini mandarini deleted the fix/firefox-issue branch February 12, 2026 09:50
GuzekAlan added a commit to software-mansion-labs/supabase-js that referenced this pull request Feb 19, 2026
* fix(postgrest): enforce type safety for table and view names in from() method (supabase#2058)

* docs(auth): clarify updateUserById does not trigger client listeners (supabase#2114)

* fix(auth): resolve Firefox content script Promise.then() security errors in locks (supabase#2112)

* build(deps): bump qs from 6.14.1 to 6.14.2 in the npm_and_yarn group across 1 directory (supabase#2118)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(release): version 2.96.0 changelogs (supabase#2121)

Co-authored-by: supabase-releaser[bot] <supabase-releaser[bot]@users.noreply.github.com>

* docs(supabase): document UNUSED_EXTERNAL_IMPORT build warning as false positive (supabase#2122)

* feat(auth): add skipAutoInitialize option to prevent constructor auto-init (supabase#2123)

* chore(release): version 2.97.0 changelogs (supabase#2124)

Co-authored-by: supabase-releaser[bot] <supabase-releaser[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Vaibhav <[email protected]>
Co-authored-by: Katerina Skroumpelou <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>
Co-authored-by: supabase-releaser[bot] <supabase-releaser[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auth-js Related to the auth-js library.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: Permission denied to access property "then" for Firefox Extensions

3 participants