Skip to content

fix(auth): raise AuthWeakPasswordError for responses without an error code - #1629

Open
hsusul wants to merge 1 commit into
supabase:mainfrom
hsusul:fix/auth-legacy-weak-password
Open

hsusul wants to merge 1 commit into
supabase:mainfrom
hsusul:fix/auth-legacy-weak-password

Conversation

@hsusul

@hsusul hsusul commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix.

What is the current behavior?

Fixes #1628

handle_exception has a legacy branch meant to raise AuthWeakPasswordError when an auth response reports a weak password without an error code. Its condition requires data["weak_password"] to be both a dict and a list, so it can never run:

and isinstance(data.get("weak_password"), dict)
and data.get("weak_password")
and isinstance(data.get("weak_password"), list)
and len(data["weak_password"])

It also inspects the wrong object — reasons is the list nested inside weak_password, so len(data["weak_password"]) measures the dict.

As a result, servers that don't send code / error_code (responses predating the 2024-01-01 API version, which this code explicitly intends to support) produce a generic AuthApiError, and callers lose .reasons — the application can't tell the user why the password was rejected.

The existing tests documented the defect rather than catching it: one comment reads "there's a logical error in the code ... This can never be true", and another test only reached the branch by monkeypatching isinstance and len.

What is the new behavior?

The legacy branch validates weak_password["reasons"] as a non-empty list of strings and returns AuthWeakPasswordError with those reasons, matching auth-js. Malformed payloads (missing/empty/non-list/non-string reasons) still fall through to AuthApiError.

handle_exception lives in the shared helpers.py, so sync and async both pick this up; no generated _sync code is affected.

Tests

  • test_handle_exception_legacy_weak_password_without_error_code — new regression test; fails on main (AuthApiError instead of AuthWeakPasswordError), passes here.
  • test_handle_exception_legacy_weak_password_ignores_malformed_reasons — parametrized negative cases.
  • test_handle_exception_weak_password_branch — dropped the isinstance/len monkeypatching now that the branch is reachable normally, and asserted reasons.
  • test_handle_exception_with_weak_password_attribute — updated the comment that described the defect.

Validation

  • uv run --package supabase_auth pytest src/auth/tests/test_helpers.py — 28 passed.
  • uv run --package supabase_auth mypy src/auth/src/supabase_auth src/auth/tests — success, 36 source files.
  • uv run ruff check --fix / uv run ruff format — clean.
  • git diff --check — clean.

The rest of the supabase_auth suite (tests/_async, tests/_sync) is integration-only and requires the GoTrue container from src/auth/infra; those tests fail with ConnectError: [Errno 61] Connection refused both with and without this change in my environment and were not run against a live server.

Additional context

None.

… code

The legacy branch in handle_exception required data["weak_password"] to be
both a dict and a list, so it could never run, and it inspected the
weak_password object where it meant to inspect its nested reasons list.
Auth responses that report a weak password without a code/error_code were
therefore downgraded to a generic AuthApiError and callers lost .reasons.

Validate weak_password["reasons"] as a non-empty list of strings instead,
matching auth-js.

Fixes supabase#1628
@hsusul
hsusul requested review from a team and o-santi as code owners September 10, 2026 03:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

handle_exception never raises AuthWeakPasswordError for responses without an error code

1 participant