Skip to content

Passwordless authentication: OTP, SSO, and OAuth/OIDC

When you stop asking users for a password, what do you verify instead? Three answers — OTP proves channel ownership, SSO and OAuth/OIDC delegate identity to a trusted issuer.

Updated May 25, 2026

What “passwordless” actually means

A passwordless login doesn’t store, transmit, or ask the user to recall a secret string. It still authenticates — but the thing being verified shifts from “do you know the password?” to one of two new questions:

  • Do you control this channel? — the server sends a one-time code or link to an address (phone number, email, push notification) that only the real user can read. Reading it back proves possession.
  • Does someone we trust already vouch for you? — the server doesn’t authenticate the user at all. It hands the job off to an Identity Provider (Google, Okta, your corporate SSO) and accepts the IdP’s signed answer.

Both shapes remove the highest-friction, highest-risk piece of password auth: the user-managed secret. They replace it with a different ceremony, and each ceremony has its own attack surface.

Concept

Passwordless authentication

Auth schemes that verify possession of a channel or trust in a third-party issuer instead of asking the user to recall a stored secret.

The category includes one-time codes (OTP), magic links, push approvals, WebAuthn / passkeys, and federation via SSO or OAuth/OIDC. They share one property: the server never compares a user-typed string against a stored hash. What it verifies — and who it trusts to vouch — is the design choice.

The three shapes most apps actually ship

One-Time Password (OTP)

Send a short code via SMS, email, or an authenticator app. The user reads it and types it back, proving they own the channel. The code is single-use and expires in minutes.

Two-step login flow
POST /login/start         → 200 { challenge_id: "abc..." }   server SMSes 6-digit code
POST /login/verify        → 200 + session cookie / token
  { challenge_id, code }

It’s familiar — every bank uses it as a step-up for transfers. The weak link is the channel itself: SMS is vulnerable to SIM-swap, email-as-channel is only as strong as the user’s email password, and any OTP that isn’t bound to the originating session can be relayed by a phishing site. TOTP apps (Google Authenticator, Authy) sidestep the channel-takeover problem but introduce a different one — device loss means an account recovery flow, and account recovery is usually where the real attacker breaks in.

Step through what an email-OTP login actually does on the server — what’s stored, what’s compared, and where the session is finally minted:

Email-OTP login

The server never stores or transmits a long-lived secret. Each challenge is a single-use, short-lived code bound to a stored challenge_id. The session is only minted after a constant-time compare succeeds.

0 / 9
UserAppStoreChannel

Single Sign-On (SSO)

Inside an organisation, you don’t want a user to have a separate password for the wiki, the CI dashboard, the cloud console, and the support tool. SSO centralises identity at one Identity Provider (Okta, Azure AD, Google Workspace). Each application redirects to the IdP, the user authenticates once (with whatever the IdP requires — typically password + MFA, increasingly a passkey), and the IdP returns a signed assertion the application trusts.

The protocols are usually SAML (older, enterprise default) or OIDC (newer, OAuth-based). The application doesn’t see the user’s credentials; it sees the IdP’s signed statement that “this user is authenticated, here are their attributes, this assertion expires at T.”

The boundary is the IdP. Compromise it and every connected app is open. Configure it well — strong MFA, conditional access, session inactivity limits — and a deleted-from-IdP user loses access to every connected app within minutes.

OAuth 2.0 and OpenID Connect (OIDC)

OAuth 2.0 is an authorization framework — it gives a third-party app a scoped token to call an API on the user’s behalf. OIDC layers identity on top: alongside the access token, the IdP issues an id_token (a JWT) that asserts who the user is. “Log in with Google” is OIDC.

The user authenticates at Google. Google sends your app a signed assertion saying “this is alice@example.com, verified email, expires in 1h”. Your app reads the id_token claims and creates (or finds) a local user. No password ever crosses your servers; you trust Google to do the authenticating.

The flow that does this — the OAuth 2.0 authorization code grant — has seven specific steps and a redirect dance that exists for a precise reason. That’s the subject of 0018 OAuth 2.0 authorization code flow, step by step · System Design .

Glossary — the terms that keep coming back

IdP (Identity Provider)
The service that authenticates users and issues assertions. Google, Okta, Azure AD, Auth0, your in-house Keycloak.
RP (Relying Party)
The application that trusts the IdP's assertion — i.e., you, the developer integrating SSO or OAuth.
OAuth 2.0
Authorization framework. Gets the RP a scoped access token to call an API on the user's behalf. Does not, by itself, authenticate the user.
OIDC (OpenID Connect)
Identity layer on top of OAuth 2.0. Adds an id_token (JWT) carrying user identity claims.
SAML
XML-based federation protocol. Older than OIDC; still dominant in enterprise SSO.
OTP (One-Time Password)
Single-use, short-lived code delivered out-of-band (SMS, email, authenticator app) and echoed back to prove channel possession.
TOTP
Time-based OTP generated by an app (Google Authenticator, Authy) from a pre-shared secret. No channel needed at login time.
MFA
Multi-factor authentication — combining two or more of: something you know, have, or are. OTP plus password is the canonical example.
Passkey (WebAuthn)
Public-key credential bound to the user's device. The browser signs a server-issued challenge with the private key; the server verifies with the public key. No shared secret to phish.

Picking among the three

Axis OTP / magic link SSO / OAuth
Who you trust The user's channel (phone, email) The IdP (Google, Okta, your org's AD)
User onboarding Anyone with an email or phone Only users who already have an account at the IdP
Friction at login Wait for code, switch context, type it back Click the IdP button — usually one redirect, often silent if session still valid
Attack surface SIM-swap, email account takeover, phishing relay IdP compromise, mis-scoped tokens, redirect_uri hijacking
Revocation Per-session in your DB Per-session in your DB plus whatever the IdP supports
Operational cost SMS/email per login (cents add up) Free for OAuth; SSO licensing for enterprise IdPs
Best fit Consumer apps with no existing IdP relationship; step-up MFA on top of passwords B2B/enterprise (SSO) or consumer apps where 'sign in with Google' is the dominant flow (OAuth/OIDC)

Most production apps end up offering more than one — typically a “sign in with Google/Apple” button (OAuth/OIDC) for friction-free onboarding, and an email-OTP fallback for users who don’t want to federate. Passwords disappear from the codebase entirely.

The mental model worth keeping

Password auth answers “do you know the secret?”. Passwordless answers a different question, and the choice of question is the design.

  • OTP answers “do you control this channel right now?” — strong if the channel is hard to take over (TOTP app, passkey), weak if it’s SMS to a phone number anyone can social-engineer a carrier into porting.
  • SSO and OAuth/OIDC answer “does a party we trust vouch for you?” — strong if the IdP is well-run with MFA, weak if it isn’t.

The deepest dive on the OAuth side is the authorization code grant — what the redirects accomplish, why the code-for-token swap happens server-to-server, and where PKCE fits in. See 0018 OAuth 2.0 authorization code flow, step by step · System Design for the step-by-step. For the other end of the spectrum — when you do keep passwords — pair 0014 Hashing passwords (not encrypting them) · System Design (hashing) and 0016 Password auth: HTTP Basic vs session cookies vs tokens · System Design (how the session is carried).