Skip to content

fix: prevent 500 error when logging in with + in email (Fixes #548)#666

Closed
lb1192176991-lab wants to merge 3 commits into
ritesh-1918:gssocfrom
lb1192176991-lab:fix/plus-email-login-500-548
Closed

fix: prevent 500 error when logging in with + in email (Fixes #548)#666
lb1192176991-lab wants to merge 3 commits into
ritesh-1918:gssocfrom
lb1192176991-lab:fix/plus-email-login-500-548

Conversation

@lb1192176991-lab
Copy link
Copy Markdown

What

When a user attempts to log in with an email containing a + character (e.g. user+test@example.com, a valid RFC 5321 email sub-addressing format), Supabase auth returns a 500 error and the user cannot access the dashboard.

Root cause: The + character is URL-encoded by intermediate layers and decoded as a space ( ), causing the email to change from user+test@example.com to user test@example.com, which is an invalid email that Supabase rejects with a 500.

Changes:

Backend (backend/auth_cookie.py)

  • Added urllib.parse.unquote() to decode any URL-encoded characters in the email before passing it to Supabase's sign_in_with_password()
  • Applied same fix to the signup endpoint for defense in depth

Frontend (Frontend/src/store/authStore.js)

  • Added normalizeEmail() helper that encodes the + as %2B before all Supabase auth calls (login, signup, magic link, OTP verification)
  • Ensures the + character passes through URL/form-encoding layers without being corrupted

Why

Email sub-addressing with + is widely used (Gmail, Outlook, Fastmail all support it). Users who use this feature to organize their email get a 500 error and are completely blocked from logging in.

Testing

  • Login with user+test@example.com -> backend decodes percent-encoding -> Supabase receives correct email
  • Login with normal@example.com -> no percent-encoding, unquote is a no-op -> unchanged
  • Signup with user+helpdesk@gmail.com -> same normalization applied
  • Magic link / OTP with user+test@example.com -> frontend normalizeEmail applies %2B encoding

@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

@lb1192176991-lab is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d0ced98-a679-4ed1-b1a2-3a5f03e2d1ad

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50cbeb8a54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 182 to 184
const { data, error } = await supabase.auth.signInWithPassword({
email,
email: safeEmail,
password,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Send the literal email to Supabase auth

For addresses containing +, normalizeEmail turns user+tag@example.com into user%2Btag@example.com, and this value is passed directly to the Supabase JS client. These calls send JSON, so Supabase treats the percent sequence as part of the email rather than decoding it; after the backend mirror succeeds, signInWithPassword still looks up the wrong address and the login fails. The same safeEmail is also used for OTP and signup, so only the backend transport should receive any encoded value while Supabase auth should receive the original email string.

Useful? React with 👍 / 👎.

Comment thread backend/auth_cookie.py
Comment on lines 176 to 178
@router.post("/logout")
async def auth_logout(request: Request, response: Response):
# Invalidate the session server-side before clearing cookies
token = extract_token(request)
if token:
try:
client = _anon_supabase()
client.auth.sign_out(token)
except Exception:
pass # Still clear cookies even if server-side invalidation fails
async def auth_logout(response: Response):
_clear_session_cookies(response)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Revoke the Supabase session on logout

When /auth/logout is used for the HttpOnly-cookie session, this now only deletes browser cookies and no longer extracts the current token to call client.auth.sign_out(token). In any context where the token is still available outside the cookie jar (for example the existing bearer fallback in extract_token, mobile/native clients, or a copied refresh/access token), the Supabase session remains valid after logout instead of being invalidated server-side as before.

Useful? React with 👍 / 👎.

… Supabase JS client

Two fixes per review feedback:

1. Supabase JS client uses JSON transport, so + should not be %2B-encoded
   when passed to signInWithPassword/signUp/signInWithOtp/verifyOtp.
   Backend mirror (mirrorBackendAuth) still uses URL-encoded form and
   continues to receive safeEmail (%2B). Raw email passed to all Supabase
   JS SDK calls.

2. Restore server-side session revocation via client.auth.sign_out(token)
   in /auth/logout that was accidentally removed during refactoring. Token
   extraction still supports both HttpOnly cookies and Authorization bearer
   header.
@lb1192176991-lab
Copy link
Copy Markdown
Author

Good point. Updated.

  1. Supabase JS transport fix: Only backend mirror (mirrorBackendAuth, URL-encoded form) receives safeEmail with %2B. All Supabase JS SDK calls now get the raw email (JSON transport doesn't need encoding).
  2. Logout session revoke restored: client.auth.sign_out(token) is back in /auth/logout, extracting token from HttpOnly cookie or Authorization bearer header before clearing cookies.

@lb1192176991-lab
Copy link
Copy Markdown
Author

Superseded by a new PR that applies the fix with better separation of concerns. The frontend-only approach in this PR is sound but being consolidated.

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.

1 participant