Summary
A connected app (an MCP client) holds a user's OAuth token. The database refuses that token on the Data API, Storage and Realtime. The Supabase Auth API is not covered. There, the token can set a password on a Google or email-link account, and password sign-in then gives a full session that survives Disconnect. The repo already has the defense, hook_block_password_tokens, but it works only when the hook is turned on in the Supabase dashboard. The public docs also claim more than the gate does.
- Severity: Medium if the hook is off; Low if it is on
- Area: Supabase Auth, connected apps, docs
- Source: security review of 2026-10-06, finding M9
Where
- The token gate covers only the Data API, Storage and Realtime:
packages/supabase/migrations/20260928120000_refuse_connected_app_tokens.sql.
- The defense:
packages/supabase/migrations/20260928130000_reject_password_sign_in_hook.sql (public.hook_block_password_tokens).
- Local config leaves the hook commented out:
packages/supabase/config.toml:265-267. secure_password_change = false at :208.
- Docs claim:
docs/mcp/reference.md:26 says the project refuses a client_id token, with no mention of the Auth API.
- Self-host docs require the hook in production:
docs/self-hosting/configuration.md (search for hook_block_password_tokens).
Fix plan
- Maintainer, Supabase dashboard (production): Authentication → Hooks. Confirm the custom access token hook points to
public.hook_block_password_tokens and is enabled. Turn on "Secure password change" (the dashboard name for secure_password_change).
docs/mcp/reference.md:26: say that the Auth API is not gated by the database, and that the password hook closes the password path. Link docs/self-hosting/configuration.md.
packages/supabase/config.toml: set secure_password_change = true, so local matches production.
Acceptance criteria
Verify
- Dashboard check by the maintainer (no API access from CI).
- Locally: with the hook enabled in
config.toml, a password sign-in for a user who only had Google sign-in is refused.
Related
Summary
A connected app (an MCP client) holds a user's OAuth token. The database refuses that token on the Data API, Storage and Realtime. The Supabase Auth API is not covered. There, the token can set a password on a Google or email-link account, and password sign-in then gives a full session that survives Disconnect. The repo already has the defense,
hook_block_password_tokens, but it works only when the hook is turned on in the Supabase dashboard. The public docs also claim more than the gate does.Where
packages/supabase/migrations/20260928120000_refuse_connected_app_tokens.sql.packages/supabase/migrations/20260928130000_reject_password_sign_in_hook.sql(public.hook_block_password_tokens).packages/supabase/config.toml:265-267.secure_password_change = falseat:208.docs/mcp/reference.md:26says the project refuses aclient_idtoken, with no mention of the Auth API.docs/self-hosting/configuration.md(search forhook_block_password_tokens).Fix plan
public.hook_block_password_tokensand is enabled. Turn on "Secure password change" (the dashboard name forsecure_password_change).docs/mcp/reference.md:26: say that the Auth API is not gated by the database, and that the password hook closes the password path. Linkdocs/self-hosting/configuration.md.packages/supabase/config.toml: setsecure_password_change = true, so local matches production.Acceptance criteria
public.hook_block_password_tokens.docs/mcp/reference.mdno longer claims a gate the Auth API does not have.Verify
config.toml, a password sign-in for a user who only had Google sign-in is refused.Related