TryMellon

Fallback by email

Fallback by email

When WebAuthn is not available, use the email fallback. All methods return Result<T, TryMellonError>.

Flow

  1. Start the flow: send OTP to the user’s email.
  2. Ask the user for the code.
  3. Verify the code and get a session token.
  4. Send the session token to your backend (same as with passkeys).

Example

// 1. Send OTP
const startResult = await client.fallback.email.start({ userId: 'user_123' })
if (!startResult.ok) {
  console.error(startResult.error)
  return
}

// 2. Get code from user
const code = prompt('Enter the code sent by email:')

// 3. Verify
const verifyResult = await client.fallback.email.verify({
  userId: 'user_123',
  code: code,
})
if (!verifyResult.ok) {
  console.error(verifyResult.error)
  return
}

// 4. Send to backend
await fetch('/api/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ session_token: verifyResult.value.sessionToken }),
})

Combined flow (passkey with fallback)

async function authenticateUser(userId: string) {
  if (!TryMellon.isSupported()) {
    return await authenticateWithEmail(userId)
  }

  const authResult = await client.authenticate({ externalUserId: userId })
  if (authResult.ok) return authResult
  if (
    authResult.error.code === 'PASSKEY_NOT_FOUND' ||
    authResult.error.code === 'NOT_SUPPORTED'
  ) {
    return await authenticateWithEmail(userId)
  }
  return authResult
}

async function authenticateWithEmail(userId: string) {
  const startRes = await client.fallback.email.start({ userId })
  if (!startRes.ok) return startRes
  const code = prompt('Enter the code sent by email:')
  return await client.fallback.email.verify({ userId, code })
}