Aug 28, 2025

Migrating from password-based to passwordless authentication

Kuntal Banerjee
Founding Engineer

In 2024, over 2.8 billion passwords ended up for sale (or free for anyone) on criminal forums. Only 3% of those met even basic complexity requirements, according to the 2025 Verizon Data Breach Investigations Report.

If you’re running a SaaS or building your own platform, you already know what that means: passwords are your weakest link.

Microsoft seems to agree. They’ve stopped asking new accounts to even set passwords. New users get passkeys, push notifications, or hardware keys by default. They’re calling it “passwordless by default”, and they’re registering almost a million passkeys a day, with a 98% sign-in success rate compared to 32% for password users.

So if Microsoft is making the jump, what about your product? How do you take users on a safe journey from password to passwordless without breaking trust or breaking logins? Let’s walk through a practical authentication migration strategy that developers can actually apply.

The passwordless spectrum

There are several ways to approach passwordless authentication.

  • Passkeys: The most secure option today, using WebAuthn and device biometrics. Strong phishing resistance and better UX on modern platforms, but might need a more complex setup to execute, and not all apps require this level of protection.
  • Magic links: Sent via email. Limitations are that they need to be opened on the same device the user needs to login from, and you should keep an eye on email deliverability, though it’s a solved problem if you’re using a tool like Scalekit.
  • OTPs (one-time passcodes): A familiar login method, with the disadvantage that SMS usually incurs costs and that the user needs to type in the OTP.
  • Push notifications: Popular with mobile-first products. Users approve sign-ins directly from their device, but require careful handling to avoid “push fatigue” where users blindly approve.

Each of these methods has trade-offs. Phasing them in during migration is a logical way to go about it. For example: you may start with magic links and OTPs, but offer passkeys as the long-term destination.

If you’d like a more detailed rundown of the different passwordless methods available, you can head over to our guide on passwordless authentication.

In this blog, we’ll take a look at the steps and considerations required to migrate from password-based to passwordless authentication.

Source: Reddit

Step 1: Assessment framework

Before writing a single line of code, you need to take stock of your existing structure.

  • User base reality check: Who are your users, and which passwordless methods will work best for them?
    • B2C SaaS with casual users may lean on magic links to ease migration.
    • Enterprise-facing apps may prioritize passkeys or OTPs from authenticator apps.
  • Entry points: Catalog every single way people sign in. Web app, native mobile app, CLI, SSO, even APIs that still depend on password headers. You don’t want to flip the switch only to realize your support team can’t log in to the admin console.
  • Risk model: What risks matter most? If phishing resistance is top priority, passkeys win. If reducing friction is your main driver, magic links might be a good step one.
  • What recovery paths exist? Can you handle “lost phone” scenarios without reverting to weak SMS resets?
  • Recovery story: If someone loses their device, can you re-enroll them securely without re-introducing SMS or email codes that attackers can phish?

The goal is to surface blockers early and write them down. Passwordless isn’t just a “flip a switch” change. It touches product, UX, support, and security policy.

Step 2: Migration timeline

Think of migration in phases, not as one big cutover:

  1. Plan: Choose your mix. Example: keep OTPs for legacy users, offer magic links for low-friction sign-in, and introduce passkeys for future-proofing.

Source: Reddit

  1. Pilot: Start with your own team or a subset of power users. Use feature flags so you can roll back quickly.
  2. Expand: Add more users. For example, roll out magic links to new sign-ups, while offering existing users a “try passwordless” option.
  3. Cutover: Once adoption looks healthy, make passwordless the default option.
  4. Stabilize: Collect data, squash edge cases, and refine helpdesk scripts.

A clear timeline gives you breathing room and avoids the panic of flipping everything at once.

Step 3: User communication strategy

End users don’t care about public key cryptography. They care about not getting locked out and not having to remember yet another password. That means your messaging should be simple:

  • “Sign in with your device. No password required.”
  • “Your fingerprint or Face ID never leaves your device.”
  • “Magic links/OTPs are safer than passwords.”

Communicate early (emails, banners, release notes) and reinforce at the moment of login. A little UX copy goes a long way. For SaaS devs, that might mean updating your onboarding flow to highlight the benefit: faster logins, fewer failed logins, and less support hassles.

Step 4: Gradual rollout plan

Let’s make this real. Below is a simple, developer-friendly path to ship passwordless with magic links and email OTPs using Scalekit. You can start with opt-in, then move cohorts over, and later make it the default.

Install and initialize: Add the SDK and create a client with your environment URL, client ID, and client secret.

npm install @scalekit-sdk/node
import { Scalekit } from '@scalekit-sdk/node'; const scalekit = new Scalekit( 'SCALEKIT_ENVIRONMENT_URL', 'SCALEKIT_CLIENT_ID', 'SCALEKIT_CLIENT_SECRET', );

Configure passwordless in the dashboard

In your Scalekit dashboard, go to Authentication → Auth methods → Passwordless, choose your mode:

  • Email OTP
  • Magic link
  • Both (link + OTP)

This determines what your users receive when you trigger the email.

Send the verification email

From your signin form, collect the user’s email and call the send API. If you enabled magic links (or link + OTP), include the magiclinkAuthUri so Scalekit can attach the link_token to that URL.

const options = { template: "SIGNIN", state: "jAy-state1-...2nqm6Q", expiresIn: 300, // required for Link or Link+OTP magiclinkAuthUri: "https://yourapp.com/passwordless/verify", templateVariables: { employeeID: "EMP523", teamName: "Alpha Team", }, }; const sendResponse = await scalekit.passwordless.sendPasswordlessEmail( "john.doe@example.com", options ); // sendResponse = { // authRequestId: string, // expiresAt: number, // expiresIn: number, // passwordlessType: "OTP" | "LINK" | "LINK_OTP" // }

Notes:

  • authRequestId ties the request together for later verification.
  • expiresIn controls the lifetime for the OTP or link.
  • You can also hit the raw REST endpoint if you prefer cURL.

Resend if needed

If the user did not receive the email or it expired, resend using the original authRequestId.

const { authRequestId } = sendResponse; const resendResponse = await scalekit.passwordless.resendPasswordlessEmail( authRequestId ); // resendResponse = { // authRequestId: "...", // expiresAt: "...", // expiresIn: "300", // passwordlessType: "OTP" | "LINK" | "LINK_OTP" // }

Pro tip: the quickstart documents a rate limit of two passwordless emails per minute per email. You can also configure “enable new passwordless credentials on resend” to issue a fresh code or link every time.

Verify with email otp

If you chose OTP or link + OTP, collect the code and verify it with the same authRequestId.

const { authRequestId } = sendResponse; const verifyResponse = await scalekit.passwordless.verifyPasswordlessEmail( { code: "123456" }, authRequestId ); // verifyResponse = { // email: "john.doe@example.com", // state: "jAy-state1-...", // template: "SIGNIN", // passwordless_type: "OTP" | "LINK" | "LINK_OTP" // }

On success, create your application session and redirect the user. If verification fails, show a friendly retry with a resend option.

Verify with a magic link

If you chose magic links or link + OTP, your user clicks a link that lands on your app with a link_token query param. Grab it and verify. If you turned on enforce same browser origin in the dashboard, include auth_request_id for extra safety.

High-level flow:

  1. User clicks link → hits your /passwordless/verify?link_token=... route
  2. Your route reads link_token (and optionally auth_request_id) and calls the verify endpoint
  3. On success, you establish a session and redirect

All of this is covered in the Scalekit quickstart’s magic link verification section, including the parameter requirements when same-origin enforcement is enabled.

Where this fits in your rollout

  • Opt-in phase: show a “sign in without a password” option that triggers the send call above.
  • Progressive nudges: after a successful password login, offer “set up passwordless” and use the same send flow to enroll.
  • Default for new signups: point your signup form to the same send flow so new accounts start passwordless on day one.
  • Safety net: keep resend and clear error states wired in so users are not stuck.

This is the migration “engine.” You can turn the knobs in the dashboard to prefer OTP, magic link, or both as you move cohorts along.

Step 5: Success metrics

Key metrics change slightly depending on your chosen methods:

  • Magic links: Link open rate, login completion rate.
  • OTPs: Code success vs error rates, SMS delivery failures.
  • Passkeys: Enrollment percentage, login success rate, fallback usage.

Across all: measure help desk volume and phishing-related incidents.

Step 6: Post-migration optimization

Passwordless isn’t “set it and forget it.” After migration:

  • Phase out passwords entirely for migrated users.
  • Encourage backups (a second device or hardware key).
  • Strengthen recovery flows without slipping back into email or SMS resets.
  • Audit accessibility: make sure sign-in is usable for people with disabilities.

Final thoughts

Passwords have been failing us for decades. With Microsoft making accounts passwordless by default and the standards now mature, we’ve reached a tipping point. This shift is not just theory.

In the real world, Fello, an AI-powered marketing engine, recently migrated to passwordless with Scalekit. Facing slow logins across a large customer base, constant engineering drain from password maintenance, and no scalable path forward, turned to Scalekit’s modular authentication platform.

"Scalekit's flexibility and speed made implementation a breeze. We got secure, scalable, passwordless auth and have the option to open up other methods like SSO as we see fit, without having to refactor the existing stack."
Suman Varanasi, CTO, Fello
Suman Varanasi
CTO, Fello

By moving to magic link and OTP-based passwordless login, they prioritized speed and control while giving their users a smoother, more reliable experience.

The migration won’t be overnight, but with a clear plan, strong communication, and the right safety nets, you can get there without breaking your product or your users’ trust.

No items found.
On this page
Share this article
Modernize login flows

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 FREE SSO/SCIM connection each
1000 Monthly active users
25 Monthly active organizations
Passwordless auth
API auth: 1000 M2M tokens
MCP auth: 1000 M2M tokens