ENGINEERING

We Killed Passwords — Here's What Happened

Andres MuguiraFebruary 26, 20266 min read
AuthMagic LinkSecurityUX
← Back to Blog
Summarize with AI

The Password Problem Nobody Talks About

Every CRM starts the same way: email field, password field, confirm password field. It is the default because every authentication library ships with it. Supabase, our auth provider, makes password-based signup a single function call. It works. It is battle-tested. And we ripped it all out.

The decision was not ideological. We did not read a blog post about passwordless being the future and jump on the bandwagon. We made this call because our support inbox told us to. In the first three weeks after launch, 40% of all support tickets were authentication related. Password resets, locked accounts, users who signed up with Google OAuth and then tried to log in with a password they never set. The pattern was unmistakable: passwords were generating more support load than every other feature combined.

When your authentication system generates more support tickets than your entire product, the authentication system is the bug.

How Magic Links Actually Work

The concept is simple. User enters their email. We send a link. They click it. They are logged in. No password to remember, no password to forget, no password to reset. But the implementation details matter enormously for security and user experience.

The SalesSheet sign-in page: just an email field and a "Send Magic Link" button. No password field anywhere.

When a user requests a magic link, our Supabase edge function generates a one-time token with a 10-minute expiration. The token is hashed with SHA-256 before storage, so even if our database were compromised, the tokens would be useless. The email contains a link to our auth callback endpoint with the raw token as a URL parameter. When the user clicks the link, we hash the incoming token and compare it against the stored hash. Match means login. Mismatch means the link was tampered with or already used.

The PKCE Mobile Extension

The full PKCE authentication flow: from user email input through magic link, code exchange, Supabase verification, to session creation.

Magic links have an inherent problem on mobile devices. When a user clicks the link in their email app, the operating system opens a browser. But which browser? On iOS, if the user's default browser is Chrome but they opened the email in the Mail app, the magic link opens in Safari. Now they are logged into a Safari session they will never use again, while Chrome, where they actually use SalesSheet, has no session.

We solved this with PKCE (Proof Key for Code Exchange). Before sending the magic link request, the client generates a random code verifier and computes a code challenge from it using SHA-256. The code challenge is sent with the magic link request. When the user clicks the link, they are redirected back to our app with an authorization code. The app then exchanges this code plus the original code verifier for a session. Because only the original client has the code verifier, only the original client can complete the login. It does not matter which browser opens the link; the session is established in the app that initiated the flow.

The implementation required a custom Supabase auth hook and modifications to our deep linking configuration on both iOS and Android. We use universal links on iOS and app links on Android to ensure the magic link always returns to our app rather than opening a random browser tab.

The "Check Your Email" confirmation screen with quick-open buttons for Gmail, Outlook, and Apple Mail.

The Recovery Token Guard

Killing passwords creates a dependency on email access. If a user loses access to their email, they lose access to their CRM. For a sales tool that contains deal pipelines, contact histories, and revenue data, this is not acceptable. We needed a recovery mechanism that does not reintroduce passwords through the back door.

When a user first logs in, we generate a 32-character recovery token and display it once. Just once. The UI makes it very clear: save this token somewhere safe, because we cannot show it again. We store a bcrypt hash of the token in the user's profile. If a user ever loses email access, they can enter their recovery token on our account recovery page. The token is verified against the stored hash, and if it matches, we allow the user to update their email address and trigger a new magic link to the new address.

This approach mirrors how cryptocurrency wallets handle seed phrases. The user holds the only copy of the plaintext. We hold only the hash. Neither party alone can compromise the account, but together they can recover it.

Why Not SMS or Authenticator Apps?

We considered both. SMS-based recovery introduces a phone number dependency and SIM-swapping risk. Authenticator apps like Google Authenticator or Authy add complexity that contradicts our entire reason for killing passwords in the first place. The recovery token is a single string that users can store in their password manager, write on a sticky note, or save in a secure note. It has zero ongoing maintenance.

The 60-Second Resend Cooldown

Magic links create a new attack surface: email bombing. Without rate limiting, an attacker could enter a victim's email address and trigger thousands of magic link emails, filling their inbox and potentially triggering spam filters that would block legitimate emails. More subtly, each magic link request generates a database write (the hashed token), so an attacker could use unlimited requests as a denial-of-service vector against our infrastructure.

Our defense is layered:

What Happened After the Switch

We deployed passwordless authentication on a Tuesday. By Friday, the results were clear:

The best security mechanism is the one that users never have to think about. Magic links are invisible security. You click, you are in, and nobody else can be.

Lessons Learned

Killing passwords was one of the best decisions we made for SalesSheet, but it required more engineering work than simply enabling a Supabase feature flag. The PKCE flow for mobile, the recovery token system, and the multi-layered rate limiting each took serious thought and testing. If you are considering passwordless for your own product, here is what we would tell you:

  1. Email deliverability is your new single point of failure. If your magic link emails land in spam, your users cannot log in. We invested in proper SPF, DKIM, and DMARC records, use a dedicated sending domain, and monitor delivery rates daily.
  2. The UX around "check your email" matters more than you think. Users are conditioned to see a login form and type a password. When you show them a "check your email" screen, some will stare at it confused. We added clear copy, a resend button with countdown, and a link to common email providers (open Gmail, open Outlook) to reduce friction.
  3. Test on every email provider. Gmail, Outlook, Yahoo, Apple Mail, ProtonMail, corporate Exchange servers. Each one renders the magic link email differently and handles link clicking differently. We found that some corporate firewalls pre-fetch URLs in emails for security scanning, which consumed the one-time token before the user could click it. We solved this by requiring a button click on the landing page rather than auto-consuming the token on page load.

Passwords are a solved problem in the sense that everyone knows how to implement them. They are an unsolved problem in the sense that users still cannot use them correctly. We chose to solve the problem by removing it entirely, and our users are better off for it.

Try SalesSheet Free

No credit card required. Start selling smarter today.

Start Free Trial