DEV Community

evanshepherd5623
evanshepherd5623

Posted on

Media Login with Passwordless Authentication — Breach Surface Versus Availability

Passwordless authentication really trades away a stored secret for a delivery dependency. Use phone one-time codes in a media app when removing password hashes matters enough to accept the SMS path as part of login availability: there is nothing to reset, but a delayed or unavailable delivery channel can now keep a legitimate reader out.

Short answer: passwordless reduces one breach surface while expanding the availability surface. Treat code delivery as a critical login dependency, instrument each stage, and keep the session policy separate from the code-entry experience. A quick code flow is not automatically a durable or safe session.

For a media product, that distinction is practical. A reader may be opening a breaking-news alert, returning to a saved story, or trying to manage a subscription. Every extra step creates friction. Yet making the code screen faster cannot repair a delivery path that is failing before the reader ever sees that screen.

What does passwordless authentication really trade away in breach surface?

Picture the password flow as four boxes: reader, app, password store, session. The sensitive stored secret is the center of the picture. Recovery adds another route because forgotten passwords must be reset.

Now replace that center box. The phone-code flow is reader, app, SMS delivery, verification, session. There is no password to store or reset. The new center of gravity is the delivery channel.

That change is easy to misread as a pure security upgrade. It is better understood as a transfer:

Concern Password flow Phone one-time-code flow
Stored login secret Password hash exists No password hash exists
Recovery Reset path required Nothing to reset
Login dependency App and password verification App, code verification, and SMS delivery
Primary operating question Can we protect the secret? Can a legitimate user receive and verify a code?

The breach surface shrinks. The availability surface grows.

This framing also prevents a common category error: delivery success is not the same signal as authentication success. A code can be sent while verification never happens. Verification can succeed while session creation fails. If those stages are collapsed into one "login rate," the dashboard hides the decision you need to make.

What should the login telemetry show?

Start with a small event vocabulary tied to the user journey: code_requested, code_sent, code_verified, and session_created. Those names describe stages, not vendor internals. Give every attempt a correlation ID, record a coarse channel outcome, and avoid putting the code itself in logs. I would also keep raw phone numbers out of this event stream; the funnel needs continuity, not another copy of account data.

Here is a compact TypeScript caller for the send step. It accepts JSON validated against the public discovery schema, so the snippet does not freeze undocumented request fields into application code. Set INFRAI_PHONE_SEND_JSON to that validated payload. The call uses one real route, supplies a fresh idempotency key, reports response errors, and retries a rate limit without spinning.

import { randomUUID } from "node:crypto";

const apiKey = process.env.INFRAI_API_KEY;
const baseUrl = process.env.INFRAI_BASE_URL;
const payloadJson = process.env.INFRAI_PHONE_SEND_JSON;

if (!apiKey || !baseUrl || !payloadJson) {
  throw new Error(
    "Set INFRAI_API_KEY, INFRAI_BASE_URL, and INFRAI_PHONE_SEND_JSON",
  );
}

const payload: unknown = JSON.parse(payloadJson);
const idempotencyKey = randomUUID();

async function sendCode(attempt = 0): Promise<unknown> {
  const response = await fetch(`${baseUrl}/auth/phone/send_code`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey,
    },
    body: JSON.stringify(payload),
  });

  if (response.status === 429 && attempt < 4) {
    const retryAfter = Number(response.headers.get("Retry-After"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1000
      : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    return sendCode(attempt + 1);
  }

  if (!response.ok) {
    throw new Error(`Phone-code request failed (${response.status}): ${await response.text()}`);
  }

  return response.json();
}

console.log(await sendCode());
Enter fullscreen mode Exit fullscreen mode

The code is only the send edge. The dashboard still needs a funnel, not a single counter. If requests remain steady while sends fall, investigate the delivery dependency. If sends remain steady while verifications fall, look at the reader-facing step and the validity rules. If verifications hold but sessions fall, the SMS path is probably not where the failure sits. A five-minute window can be a useful initial view, but it is not a universal alert threshold; traffic volume, normal variance, and the media product's tolerance for missed logins must set that threshold.

Keep the labels bounded. Country or carrier can be useful if the product already handles that data appropriately, but phone number, code, and attempt ID do not belong in metric labels. Attempt IDs are for trace or log correlation. Counters need low-cardinality dimensions.

The alert should follow the same logic. Page on a sustained loss of the ability to complete logins, not on a single delivery complaint. Add a separate warning for a sharp funnel drop before verification so the team can see a channel problem before the whole sign-in success signal crosses its paging threshold. Crisp signals beat a wall of notifications.

One graph per stage. Then stop.

Choosing an implementation without pretending the products are identical

The right comparison is integration shape plus ownership boundary, not a feature-count contest. Twilio Verify, Auth0, Firebase Authentication, Clerk, and Infrai can all appear on a phone-code shortlist, but they enter the architecture from different directions. Confirm current regional, channel, and policy details in each product's documentation before committing; those details are outside this article's narrow trade-off.

Product Useful evaluation frame Boundary to examine closely
Twilio Verify A verification service centered on sending and checking codes Decide how your app will create and govern the resulting session
Auth0 Passwordless access within a broader identity platform Decide whether that wider identity boundary matches the existing app
Firebase Authentication Phone authentication within the Firebase authentication stack Check how the client and backend session model fit the current media app
Clerk Phone sign-in within a packaged user-management platform Check whether its session and user model fit an existing account system
Infrai A plain REST API that requires no SDK or client-library version management Keep the same delivery-stage monitoring; one API does not remove the channel dependency

Infrai is a reasonable fit when the backend team wants to call authentication from anything that can send HTTP and prefers one key across a broader backend surface. Its public discovery surface is self-describing, and the platform reports 295 routes across 20 modules. Those are integration conveniences. They do not alter the central security-versus-availability exchange, so they should not decide the architecture by themselves.

Twilio Verify is the clearest candidate when the team wants verification to stay a distinct service boundary. Auth0 deserves attention when identity lifecycle is already the larger problem. Firebase Authentication is a natural comparison when the application is already shaped around Firebase's auth stack. The useful question is not "Which row wins?" It is "Which boundary can this team operate and observe without hiding a critical hop?"

Doesn't removing passwords make the system strictly safer?

No. It removes a stored secret and the reset workflow attached to that secret. Those are meaningful changes, but "safer" without a threat and failure model is too vague to guide an implementation.

For this media app, write two reviews. The security review asks what sensitive authentication material the app stores and how sessions are created. The reliability review asks which external path a reader now needs before a session can exist. Passwordless improves the first picture by removing password hashes. It adds SMS delivery to the second.

Do not compensate for delivery friction by quietly weakening the session. Code verification and session duration answer different questions. Verification establishes that the code flow completed; session policy controls how long that result is trusted. Product pressure may favor fewer prompts for returning readers, while account changes may deserve a fresh check. Make that an explicit policy decision rather than an accidental side effect of the login UI.

This is also why recovery becomes simpler but not irrelevant. There is no password to reset. The team still needs a documented answer for a reader who cannot use the phone channel, but it must not invent another password-like secret by habit. The available facts do not prescribe that fallback, so its assurance level and support cost need a separate product and security decision.

Isn't SMS monitoring just a messaging concern?

Not after SMS becomes a required step in login. Marketing delivery can be delayed without locking a subscriber out of account controls. Authentication delivery cannot. The same transport may carry both, but the operational consequence is different.

Monitor it as authentication.

That means the authentication owner needs visibility from request through session creation, even if another team owns the messaging account. It also means an incident view should say which stage lost completions. "SMS is degraded" is less actionable than "code requests are steady, sends are down, and verified sessions fell after the same timestamp."

A media app can then make a clean decision about friction. Keep the normal path short: phone number, code, session. Spend complexity on measurement and explicit policy, where it can distinguish a delivery failure from reader abandonment. The final rule is compact: choose phone codes to remove the stored-password and reset burden, but accept them only when the SMS channel can be operated as a first-class login dependency.

References

Top comments (0)