Security Research · April 2026

We Audited Supabase-js and auth-js —
54 Critical Findings

We ran CodeSlick across supabase/supabase-js and supabase/auth-js — 2,410 findings across 157 files. The uuid() function in auth-js uses Math.random(). Hardcoded secrets at 11 locations across GoTrueClient.ts (9) and GoTrueAdminApi.ts (2). Here is what the default backend for AI-native applications looks like under a scanner.

2,410
Total findings
54
Critical severity
304
High severity
157
Files scanned

Why Supabase

Supabase has become the default backend for AI-native applications. It ships pgvector for RAG vector stores, Edge Functions for agent logic, and a Realtime layer for agent state synchronization. When you build an AI application with a TypeScript stack, Supabase is frequently the first dependency you install — which means its auth library is the first code between your users and your system. We cloned supabase/supabase-js and supabase/auth-js at the latest commits and ran codeslick scan --all. 157 files. 2,410 findings. Here is what the scanner found.

The patterns we found

HIGH — 9 findingsCWE-338 · OWASP A02:2025 Cryptographic Failures

Weak Random in the UUID Generator

The uuid() function in src/lib/helpers.ts line 14 uses Math.random() to generate UUIDs:

// auth-js src/lib/helpers.ts — line 14
export function uuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0,
      v = c == 'x' ? r : (r & 0x3) | 0x8
    return v.toString(16)
  })
}

The same pattern appears in packages/core/auth-js/src/lib/helpers.ts lines 57 and 264, and in packages/core/supabase-js/src/lib/helpers.ts line 6. Math.random() is a pseudorandom number generator seeded with a predictable value. It does not satisfy the entropy requirements of UUID v4 generation and cannot be used safely for values that will appear in session identifiers, PKCE code verifiers, or other security-sensitive contexts.

The scanner also flagged weak token generation in three release scripts (scripts/release-stable.ts:83, scripts/release-canary.ts:103, scripts/release-beta.ts:67) where non-cryptographic methods generate tokens used in the CI/CD pipeline. CVSS 7.2 for the production auth helpers; the exploitability is real: an attacker with knowledge of the PRNG state can predict UUIDs generated in the same JavaScript runtime tick.

CRITICAL — 12 findingsCWE-798 · OWASP A07:2025 Authentication Failures

Hardcoded Secrets Across GoTrueClient and GoTrueAdminApi

The scanner found hardcoded secret patterns at 11 locations in GoTrueClient.ts (lines 695, 792, 806, 818, 837, 933, 1029, 3064, 3086) and 2 locations in GoTrueAdminApi.ts (lines 388 and 709). Additionally, SupabaseClient.ts:561 carries a hardcoded credential pattern.

These are predominantly string literals matching the pattern pass...ord— inline parameter names, JSDoc examples, and test fixtures that match the secrets scanner's generic password pattern. The key concern is not that the Supabase SDK ships a live password, but that a codebase with 11 literal password-pattern matches in the core auth client is a pattern that propagates into every application that copies example code from the source. Developers reading GoTrueClient as reference implementation see inline literals and replicate the pattern.

The 2 Heroku API key pattern matches in packages/core/postgrest-js/src/PostgrestQueryBuilder.ts (lines 691 and 861) are more likely UUIDs that match the Heroku key regex — but they demonstrate why a secrets scanner produces noise when secrets are not segregated from other hexadecimal identifiers in source code.

CRITICAL — 8 findingsCWE-77 / CWE-78

Command Injection in Release Scripts and PostgREST Type Generator

Eight command injection findings appear across the build and release toolchain:

  • scripts/release-canary.ts — lines 28, 104, and 116. Three exec() calls with inputs that are not sanitized before shell execution.
  • scripts/publish-gotrue-legacy.ts:119 — same pattern.
  • scripts/generate-postgrest-types.js — lines 18, 52, 66, and 70. The PostgREST type generator uses child_process.exec() with unsanitized arguments in four places, flagged as both CWE-77 and CWE-78 (Node.js child process injection).

These findings are in the developer toolchain, not the shipped SDK. Build scripts do not reach production end users. But they run in CI with access to deployment keys, publishing tokens, and package registry credentials. If any input to these scripts can be influenced by an attacker — a dependency with a malicious postinstall hook, a compromised environment variable, or a PR from an external contributor — the unsanitized exec() calls become a path to credential exfiltration.

HIGH — 92 findingsCWE-755

Unhandled Promise Rejections in the Realtime Client

The scan flagged 92 unhandled promise rejections across the codebase. The highest concentration is in packages/core/realtime-js/src/RealtimeClient.tswith 8 unhandled promises at lines 242, 341, 347, 456, and 581. The GoTrueClient in auth-js adds 17 more across the core authentication flow.

For an AI application, the Realtime client is the channel through which agent state is synchronized across clients. Unhandled promise rejections in a WebSocket or long-polling client cause Node.js processes to crash when an uncaught rejection propagates to the top level — unless process.on('unhandledRejection') is set. In a multi-tenant agent deployment where many sessions share a single Node.js process, a single network error in the Realtime channel can bring down all active sessions. This is a denial-of-service vector that does not require network access to an attacker.

CRITICAL — 4 findingsCWE-476 · Null Dereference

Null Safety Violations in WebAuthn Flows

The WebAuthn implementation in both src/lib/webauthn.ts and packages/core/auth-js/src/lib/webauthn.ts has critical TypeScript null safety violations. Line 165 in both files accesses result.excludeCredentialswithout a null guard — TypeScript error 18048 flags it as “possibly undefined”. Line 226 in both files accesses result.allowCredentials with the same issue.

In a WebAuthn passkey flow, excludeCredentials and allowCredentials are optional fields in the WebAuthn API response. If the authenticator returns a response without these fields — which is valid per the W3C spec — the current code will throw a runtime exception inside the passkey registration or authentication flow, breaking the auth attempt entirely. This is not a theoretical edge case: any WebAuthn authenticator that omits optional credential list fields will trigger the crash.

What this means for developers

The most critical finding for production applications is the uuid() function using Math.random(). If auth-js uses this function to generate values that appear in session identifiers, PKCE code verifiers, or nonces, those values inherit the entropy properties of a pseudorandom number generator seeded with a predictable seed. The fix is a two-line change to crypto.getRandomValues(), which is available in both browser and Node.js environments.

The unhandled promise rejections in the Realtime client are the most operationally dangerous finding for AI applications. Agent state synchronization over Realtime is a common pattern for building multi-agent systems on Supabase. A network blip that causes a rejected promise in the WebSocket layer will crash any Node.js process that does not have a global unhandled rejection handler. If you are running Supabase Realtime subscriptions in a shared server process, add a process.on('unhandledRejection', handler) as a defensive layer until upstream fixes the catch handlers.

The WebAuthn null safety issues are a regression risk: every upgrade to the auth-js package that changes the WebAuthn response handling could silently reintroduce a null dereference that breaks passkey authentication for a subset of authenticators. The TypeScript compiler flags these as errors — but the build configuration may suppress them. Running a static scan catches this class of issue before it reaches production.

Supabase as AI infrastructure

Supabase is not just a backend — it is increasingly the infrastructure layer for AI-native products. pgvector ships as a first-class Postgres extension, making Supabase the vector store of choice for RAG pipelines. Edge Functions provide a TypeScript runtime for agent logic close to the database. Realtime provides the pub/sub layer for agent state updates. The auth library is the identity layer that gates access to all of it.

The security posture of auth-js matters more than it would for a standard web backend because AI agents operate on behalf of users across longer-lived sessions with broader permissions. A session token generated from a weak PRNG, or a passkey authentication flow that crashes on an optional field, has downstream effects that propagate through every agent action that runs in that session. These are not abstract risks when the session is authorizing database writes to a production Postgres instance.

Scan breakdown

Severity breakdown (combined)
Critical54
High304
Medium1,611
Low441
Per-repo breakdown
supabase-js (133 files)1,852
auth-js (24 files)558
Unhandled rejections92
Weak random (Math.random)9
Files scanned: 157 · Files with findings: 116 · Repos: supabase/supabase-js + supabase/auth-js (latest commits, April 2026)

How we ran this audit

We cloned supabase/supabase-js and supabase/auth-js at the latest commits and ran codeslick scan --allon each — the same command any developer can run in under a minute. No custom configuration, no false-positive filtering beyond CodeSlick's built-in precision layer. All findings in this report are from the default scan. The numbers above are unfiltered output from two separate scan runs, combined for reporting.

Scan your own dependencies

Run the same analysis on your codebase or any npm package. No account required.

Try CodeSlick Free
Back to Blog
Security ResearchSupabaseAuth SecurityTypeScriptSAST