Skip to content

feat: add email-only sign-in option with dynamic wallet ordering#308

Merged
braianj merged 24 commits intomainfrom
feat/sign-in-primary-option
Mar 6, 2026
Merged

feat: add email-only sign-in option with dynamic wallet ordering#308
braianj merged 24 commits intomainfrom
feat/sign-in-primary-option

Conversation

@braianj
Copy link
Copy Markdown
Contributor

@braianj braianj commented Mar 3, 2026

This PR implements support for the email-only sign-in feature with dynamic wallet button ordering based on MetaMask
availability and user preferences.

New Features

Email-only sign-in mode (isOnlyEmailOption flag):

  • One-wallet mode (isSignInWithTwoOptions: false): Shows only MetaMask if available
  • Two-wallet mode (isSignInWithTwoOptions: true): Shows Google + MetaMask if available

Dynamic wallet ordering:

  • New useWalletOptions hook centralizes wallet ordering logic
  • Prioritizes Google and MetaMask based on feature flags
  • Gracefully handles missing wallet providers
  • Created useWalletOptions hook to encapsulate wallet ordering logic

Behavior

Default (flag disabled):
Wallet options match current/legacy behavior

Email-only mode with one wallet (isOnlyEmailOption: true, isSignInWithTwoOptions: false):

  • With MetaMask: Only MetaMask shown
  • Without MetaMask: No primary buttons, all in "More options" (Google first, MetaMask second)

Email-only mode with two wallets (isOnlyEmailOption: true, isSignInWithTwoOptions: true):

  • With MetaMask: Google first, MetaMask second
  • Without MetaMask: Only Google shown, MetaMask in "More options"

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 3, 2026

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

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment Mar 6, 2026 9:28pm

Request Review

@braianj braianj self-assigned this Mar 3, 2026
@braianj braianj added the enhancement New feature or request label Mar 3, 2026
Copy link
Copy Markdown
Collaborator

@LautaroPetaccio LautaroPetaccio left a comment

Choose a reason for hiding this comment

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

Great work! 👍 I've left a couple of comments to review

? connectionOptions?.extraOptions?.[0]
: connectionOptions?.secondary
const filteredExtraOptions = connectionOptions?.extraOptions?.filter(opt => opt !== ConnectionOptionType.EMAIL)
const { firstWalletOption, secondWalletOption, remainingWalletOptions } = useWalletOptions({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Be aware that the changes in ConnectionSecondaryButton and in Connection do impact the mobile site for Regenesis. Please check with them before merging.

Comment on lines +20 to +21
const isMetamask = option === ConnectionOptionType.METAMASK
const shouldDisableMetamask = isMetamask && !isMetamaskAvailable
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function seems to be the same as the one used in the ConnectionPrimaryButton, what do you think about abstracting it?

Comment on lines +70 to +71
const isOnlyEmailOption = flags[FeatureFlagsKeys.SIGN_IN_PRIMARY_OPTION]
const isSignInWithTwoOptions = variants[FeatureFlagsKeys.SIGN_IN_PRIMARY_OPTION]?.name === SignInPrimaryOption.TWO_OPTIONS
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What if we have a signIgnOptions variable that has the values of one or two, for the options instead of two variables that get passed to the component?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'mm make a refactor

Comment thread src/hooks/useWalletOptions.ts Outdated
)

// Current/Old behavior: show all options normally (when isOnlyEmailOption is disabled)
if (!isOnlyEmailOption) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this correct? We're not using this logic in production, we're always showing the Metamask login as the primary action or am I missing something?

Comment thread src/components/Connection/Connection.spec.tsx Outdated
LautaroPetaccio and others added 5 commits March 6, 2026 17:17
* fix: Incorrect handling of callback and magic re-log in

* fix: Ignore error

* Ignore MISSING_PKCE_METADATA error
…in the wallet (#321)

* fix: prevent reporting to Sentry an error when the user rejects a tx in the wallet

* fix: user rejected_action report to Sentry

* refactor: PR feedback

* chore: indent comment
…to extract params (#329)

* fix: SSO log-in with Magic by waiting for redirect url before trying to extract params

* fix: apply PR feedback

---------

Co-authored-by: Lautaro Petaccio <1120791+LautaroPetaccio@users.noreply.github.com>
Copy link
Copy Markdown

@regenesis-claw regenesis-claw left a comment

Choose a reason for hiding this comment

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

🚨 Breaking change for /auth/mobile route

The new useWalletOptions hook in Connection.tsx replaces the old inline wallet-filtering logic, but it drops the primary field entirely — it only considers secondary and extraOptions. This works for the desktop LoginPage (which always sets primary: EMAIL), but breaks all mobile configs where primary is a non-EMAIL option.

How it breaks

MobileProviderSelection renders <Connection> without passing signInOptionsMode, so the hook defaults to FULL mode:

// FULL mode in useWalletOptions:
firstWalletOption: connectionOptions?.secondary,
secondWalletOption: connectionOptions?.extraOptions?.[0],
remainingWalletOptions: connectionOptions?.extraOptions?.slice(1)

The primary field is completely ignored.

Affected mobile configs (targetConfig.ts):

Config primary Before After
iOS APPLE APPLE shown as first button ❌ APPLE gone — shows WALLET_CONNECT instead
Android GOOGLE GOOGLE shown as first button ❌ GOOGLE dropped — shows WALLET_CONNECT instead
androidWeb3 WALLET_CONNECT (no secondary) WALLET_CONNECT shown Nothing shown (secondary is undefined)
androidSocial EMAIL Works ✅ OK (primary is EMAIL)

Old logic (worked correctly for mobile):

const filteredPrimary =
  connectionOptions?.primary === ConnectionOptionType.EMAIL
    ? connectionOptions?.secondary
    : connectionOptions?.primary  // <-- kept non-EMAIL primary

The old code correctly fell through to use primary when it wasn't EMAIL. The new hook assumes primary is always EMAIL and discards it.

Suggested fix

The useWalletOptions hook needs to handle the case where primary is not EMAIL. In FULL mode (which is what mobile uses), if primary !== EMAIL, it should include primary in the wallet options — matching the old behavior. Alternatively, MobileProviderSelection could pass a dedicated mode or the mobile configs could be restructured.

Minor: getSignInOptionsMode doesn't check variant.enabled

If a variant exists but is disabled (enabled: false), getSignInOptionsMode would still return ONE instead of FULL:

if (!variant) {
  return SignInOptionsMode.FULL
}
// No check for variant.enabled here

Minor: Unused type addition

shouldShowGoogleOptionAsPrimary was added to ConnectionProps but doesn't appear to be used anywhere in this PR.


Requested by Gon via Slack

if (!signInOptionsMode || signInOptionsMode === SignInOptionsMode.FULL) {
return {
firstWalletOption: connectionOptions?.secondary,
secondWalletOption: connectionOptions?.extraOptions?.[0],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 Bug — breaks mobile auth: In FULL mode, primary is completely ignored. The old code in Connection.tsx had:

const filteredPrimary = connectionOptions?.primary === ConnectionOptionType.EMAIL
  ? connectionOptions?.secondary
  : connectionOptions?.primary  // kept non-EMAIL primary!

The mobile configs (ios, android, androidWeb3) set primary to APPLE, GOOGLE, or WALLET_CONNECT respectively. With this new code, those primary login options silently disappear from the UI.

This needs to include primary in the wallet options when primary !== EMAIL.

function getSignInOptionsMode(variants: Partial<FeatureFlagsVariants>): SignInOptionsMode {
const variant = variants[FeatureFlagsKeys.SIGN_IN_PRIMARY_OPTION]

// If variant doesn't exist, use full mode (legacy behavior)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: This doesn't check variant.enabled. If a variant exists but has enabled: false, it would still fall through to return ONE instead of FULL.

Suggestion:

if (!variant || !variant.enabled) {
  return SignInOptionsMode.FULL
}

type MetamaskEthereumWindow = typeof window.ethereum & { isMetaMask?: boolean }

type ConnectionProps = {
shouldShowGoogleOptionAsPrimary?: boolean
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: shouldShowGoogleOptionAsPrimary is added here but doesn't appear to be used anywhere in this PR. Leftover?

@braianj braianj requested a review from regenesis-claw March 6, 2026 21:30
@braianj braianj merged commit 0629665 into main Mar 6, 2026
9 checks passed
@braianj braianj deleted the feat/sign-in-primary-option branch March 6, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants