Skip to content

refactor: Convert Supabase Auth from library UI to custom components#1969

Open
dan-lee wants to merge 3 commits intomainfrom
claude/supabase-custom-auth-ui-suMru
Open

refactor: Convert Supabase Auth from library UI to custom components#1969
dan-lee wants to merge 3 commits intomainfrom
claude/supabase-custom-auth-ui-suMru

Conversation

@dan-lee
Copy link
Contributor

@dan-lee dan-lee commented Feb 13, 2026

Replace @supabase/auth-ui-react with custom UI components matching the Firebase Auth implementation. This provides:

  • Consistent UX across all auth providers using shared ZudokuAuthUi
  • Direct Supabase client API calls for all auth operations
  • Custom error message mapping for better user experience
  • Support for email/password sign in/up, OAuth, password reset, and email verification flows

Changes:

  • Update supabase.tsx to use ZudokuSignInUi, ZudokuSignUpUi, ZudokuPasswordResetUi, and EmailVerificationUi components
  • Add getSupabaseErrorMessage function for user-friendly error messages
  • Remove SupabaseAuthUI.tsx and @supabase/auth-ui-* dependencies

https://claude.ai/code/session_01Hq3vMiJDcKrU8NSHyvmdZS

@dan-lee dan-lee added the feature Feature label Feb 13, 2026
@vercel
Copy link

vercel bot commented Feb 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
zudoku-cosmo-cargo Ready Ready Preview, Comment Feb 13, 2026 10:44am
zudoku-dev Ready Ready Preview, Comment Feb 13, 2026 10:44am

Request Review

@github-actions
Copy link

github-actions bot commented Feb 13, 2026

Preview build of published Zudoku package for commit 979cc50.

See the deployment at: https://996d9849.cosmocargo-public-package.pages.dev

Note

This is a preview of the Cosmo Cargo example using the Zudoku package published to a local registry to ensure it'll be working when published to the public NPM registry.

Last updated: 2026-02-13T10:46:09.647Z

Replace @supabase/auth-ui-react with custom UI components matching
the Firebase Auth implementation. This provides:

- Consistent UX across all auth providers using shared ZudokuAuthUi
- Direct Supabase client API calls for all auth operations
- Custom error message mapping for better user experience
- Support for email/password sign in/up, OAuth, password reset,
  and email verification flows

Changes:
- Update supabase.tsx to use ZudokuSignInUi, ZudokuSignUpUi,
  ZudokuPasswordResetUi, and EmailVerificationUi components
- Add getSupabaseErrorMessage function for user-friendly error messages
- Remove SupabaseAuthUI.tsx and @supabase/auth-ui-* dependencies

https://claude.ai/code/session_01Hq3vMiJDcKrU8NSHyvmdZS
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the Supabase authentication provider to stop relying on @supabase/auth-ui-react and instead use the shared custom auth UI components already used by the Firebase provider, aiming for consistent UX and more control over auth flows and errors.

Changes:

  • Removed @supabase/auth-ui-react / @supabase/auth-ui-shared usage and deleted the SupabaseAuthUI wrapper component.
  • Updated the Supabase auth provider routes to use ZudokuSignInUi, ZudokuSignUpUi, ZudokuPasswordResetUi, and EmailVerificationUi.
  • Added Supabase-specific error message mapping via getSupabaseErrorMessage.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.

File Description
pnpm-lock.yaml Removes lock entries for Supabase Auth UI deps from the zudoku package (still present elsewhere in the repo).
packages/zudoku/src/lib/authentication/providers/supabase/SupabaseAuthUI.tsx Deletes the Supabase Auth UI wrapper that depended on @supabase/auth-ui-*.
packages/zudoku/src/lib/authentication/providers/supabase.tsx Switches Supabase auth routes to shared custom UI, adds email verification + password reset wiring, and introduces Supabase error mapping.
packages/zudoku/package.json Removes optional deps on @supabase/auth-ui-react and @supabase/auth-ui-shared.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +52
// Support both 'provider' (deprecated) and 'providers' config
const configuredProviders = config.provider
? [config.provider]
: (config.providers ?? []);
this.providers = configuredProviders;
this.enableUsernamePassword = !config.onlyThirdPartyProviders;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

config.providers for Supabase is currently an unvalidated string[], but ZudokuSignInUi/ZudokuSignUpUi will throw at runtime for any provider ID not in its supported list (e.g., Supabase docs commonly use azure, discord, etc.). To avoid breaking existing Supabase configurations, either validate/map the configured providers to what the UI supports (with a clear error message), or extend the shared auth UI to render unsupported provider IDs generically instead of throwing.

Copilot uses AI. Check for mistakes.
Comment on lines 169 to 215
options: {
emailRedirectTo: `${window.location.origin}${this.config.basePath ?? ""}/verify-email`,
},
});
useAuthState.setState({ isPending: false });
if (error) {
throw Error(getSupabaseErrorMessage(error), { cause: error });
}

// If user exists and is confirmed, update state
if (data.user) {
const profile: UserProfile = {
sub: data.user.id,
email: data.user.email,
name: data.user.user_metadata.full_name || data.user.user_metadata.name,
emailVerified: data.user.email_confirmed_at != null,
pictureUrl: data.user.user_metadata.avatar_url,
};

useAuthState.getState().setLoggedIn({
profile,
providerData: { session: data.session },
});
}
};

private onOAuthSignIn = async (providerId: string) => {
useAuthState.setState({ isPending: true });
const { error } = await this.client.auth.signInWithOAuth({
provider: providerId as Provider,
options: {
redirectTo:
this.config.redirectToAfterSignIn ??
`${window.location.origin}${this.config.basePath ?? ""}`,
},
});
if (error) {
useAuthState.setState({ isPending: false });
throw new AuthorizationError(error.message);
}
// Note: OAuth sign-in redirects the page, so isPending stays true
};

private onPasswordReset = async (email: string) => {
const { error } = await this.client.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}${this.config.basePath ?? ""}/reset-password`,
});
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Building redirect URLs via string concatenation with this.config.basePath (e.g. ${window.location.origin}${this.config.basePath ?? ""}/verify-email) is brittle if basePath is configured without a leading slash or with a trailing slash. Prefer using the existing joinUrl helper (used by other auth providers) to compose these URLs safely and consistently.

Copilot uses AI. Check for mistakes.
Comment on lines +178 to +192
// If user exists and is confirmed, update state
if (data.user) {
const profile: UserProfile = {
sub: data.user.id,
email: data.user.email,
name: data.user.user_metadata.full_name || data.user.user_metadata.name,
emailVerified: data.user.email_confirmed_at != null,
pictureUrl: data.user.user_metadata.avatar_url,
};

useAuthState.getState().setLoggedIn({
profile,
providerData: { session: data.session },
});
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

onUsernamePasswordSignUp calls setLoggedIn whenever data.user exists, but Supabase often returns data.user with data.session === null when email confirmation is required. That would mark the user as authenticated in Zudoku while they have no session/access token, causing protected-route flows and signRequest to fail. Only set logged-in state when a valid session exists (or after confirmation), and otherwise route the user into the email verification flow.

Copilot uses AI. Check for mistakes.
Comment on lines 212 to 218
private onPasswordReset = async (email: string) => {
const { error } = await this.client.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}${this.config.basePath ?? ""}/reset-password`,
});
if (error) {
throw Error(getSupabaseErrorMessage(error), { cause: error });
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

resetPasswordForEmail is configured to redirect users back to /reset-password after they click the email link, but the /reset-password route renders ZudokuPasswordResetUi, which only requests a reset email and doesn't handle the recovery callback / setting a new password (e.g. via auth.updateUser({ password })). This makes the Supabase password reset flow incomplete; add a dedicated recovery/new-password UI for the redirect or enhance this route to handle the recovery session/code.

Copilot uses AI. Check for mistakes.
… error

Add check for typeof window === "undefined" to handle server-side
rendering where window is not available. Also update ZudokuSignUpUi
to use getRelativeRedirectUrl consistently instead of direct
window.location.origin access.

https://claude.ai/code/session_01Hq3vMiJDcKrU8NSHyvmdZS
- Use joinUrl helper for URL construction instead of string concatenation
  for safer path joining with basePath configuration
- Add ZudokuPasswordUpdateUi component for handling password recovery
  callback flow (users clicking reset link now see "set new password"
  form instead of "enter email" form)
- Add /update-password route and onPasswordUpdate method

https://claude.ai/code/session_01Hq3vMiJDcKrU8NSHyvmdZS
@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 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.

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)

✅ Unit Test PR creation complete.

  • Create PR with unit tests
  • Commit unit tests in branch claude/supabase-custom-auth-ui-suMru
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

✅ Created PR with unit tests: #1971

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants