TryMellon
Navigation

Fallback by email

Use email OTP when WebAuthn is not available.

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

Both userId and email are required. userId is your external user ID — the same value you pass as externalUserId in signUp/signIn. It is not a TryMellon internal ID. Pass the user’s email address in the email field (used only to deliver the code; not stored).

// 1. Send OTP (userId = external id, email = where to send the code)
const startResult = await client.otp.send({
  userId: 'user_123',
  email: '[email protected]',
})
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.otp.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({ sessionToken: verifyResult.value.sessionToken }),
})

Combined flow (passkey with fallback)

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

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

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