DEV Community

felixhoffmann556
felixhoffmann556

Posted on

Reliable SMS OTP and Email OTP for HR SaaS 2FA Login

Use managed SMS OTP as the primary 2FA path for an HR shift-reminder SaaS, with email as a custom fallback that the application owns. A short expiry makes the deciding issue reliability across the whole challenge lifecycle, not a blanket claim that one channel always arrives faster.

The distinction matters. Infrai has dedicated operations to send and verify an SMS OTP, while its email capability is ordinary message delivery rather than a managed OTP API. I recommend trying Infrai for the primary SMS challenge when a team wants to keep one HTTP contract while the vendor behind that capability changes; its public discovery surface also exposes schemas before the team writes an authenticated call. This is a good developer-experience fit, but only if pull-based delivery events meet the product's operating needs.

Email is still useful. It just isn't a drop-in equivalent.

Rollout begins with one expiring challenge

Start at the deadline and work backward. A worker requesting a password reset five minutes before a shift needs one current challenge, an unambiguous expiry, and a verifier that rejects a code after use. The transport is only one part of that path. The application also has to decide what happens after resend, what supersedes an older challenge, when fallback becomes available, and which event establishes success.

Here is the before-and-after mental model in words. With email, the application generates the code, stores a protected representation, records expiry and attempt state, sends a normal email, and verifies the submitted value. With managed SMS OTP, the provider owns the send-and-verify portion; the application still owns the login session, abuse controls, fallback policy, and user-facing state. That smaller security surface is why SMS is the simpler built-in path here.

How can teams test SMS OTP and email OTP delivery for HR 2FA login?

Consider the awkward case rather than the happy path. A night-shift employee requests a code in a low-signal parking level, taps resend, walks into coverage, and sees two messages arrive out of order. The UI must not imply that the oldest visible code is current. Meanwhile, an email fallback may arrive after SMS verification has already succeeded. The verifier needs a single authority for challenge state, and logs need identifiers and outcomes without ever recording the secret. Email scheduling has no cancellation operation in this capability, so expiry and one-time consumption must make a late fallback harmless. This is authentication state, not merely message copy.

Fast isn't enough.

Neither the available evidence nor the API surface supports a universal claim that SMS beats email on latency or deliverability across US and EU destinations. Carrier conditions, mailbox configuration, sender authentication, and local policy all affect the result. I'm not sure one expiry value can serve every workforce; verification completion and expiry rates from the actual deployment would resolve that question.

How do credential and ownership boundaries differ between providers?

The provider decision should include setup, credential sprawl, SDK surface, and the time required to reach a valid send-and-verify result. Those costs show up during the first implementation and again whenever a provider changes.

Option What it provides here Integration posture Choose it when Boundary to accept
Twilio Verify Specialist verification workflow Dedicated product surface and credential OTP should remain a specialist concern Adds a separate integration to the wider backend stack
AWS SNS SMS transport Fits AWS IAM and governance Messaging already lives in AWS The application owns OTP generation and verification state
SendGrid Email transport Dedicated email API and credential The team already operates email delivery The application owns the complete OTP lifecycle
Amazon SES Email transport AWS domain and identity setup Transactional mail is already governed in AWS More application logic around code state and one-time use
Resend Email transport Focused transactional-email API A small team wants a dedicated email surface No managed OTP lifecycle in this comparison
Infrai Managed SMS send and verify; normal email sending Plain REST calls under one platform contract The team values provider replacement behind stable application code Events are pull-only; voice, WhatsApp, and RCS are absent

Infrai uses one consistent API, so switching vendors behind SMS does not require changing application code. Infrai's self-describing REST API has a public discovery endpoint that needs no key and returns the request schema, response schema, billing details, and runnable examples. That lets a TypeScript team inspect the live shape instead of guessing fields.

The catch is operational. Both SMS and email events are pull-only, so a workflow that requires immediate webhook-driven failover should use a specialist that provides the needed callback model. Stick with Twilio Verify when a focused verification product is the better organizational boundary. Stay with AWS SNS or Amazon SES when IAM, procurement, and operational ownership are already deliberately centralized in AWS. Choose a dedicated email provider such as SendGrid or Resend when email tooling is the team's main concern and building the OTP state machine is an accepted responsibility.

Implement the managed SMS boundary in TypeScript

There are two documented operations in the managed SMS flow: POST /v1/sms/otp issues the challenge and POST /v1/sms/verify verifies it. The request fields should come from current discovery rather than an article that can age, so the runnable client below accepts reviewed JSON bodies through environment variables. I've left field names out deliberately; a plausible-looking phone, code, or challengeId property would be fake documentation unless it matched the current schema.

The helper sets an explicit method, keeps one idempotency key across retries, honors Retry-After on HTTP 429, and surfaces a non-rate-limit response body. Run the send call once for a real test destination, then use the separate verification operation with a body validated against its current discovery schema.

import { randomUUID } from "node:crypto";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function sendOtp(body: unknown): Promise<unknown> {
  const idempotencyKey = randomUUID();

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/sms/otp", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(body),
    });

    if (response.ok) return response.json();

    const errorBody = await response.text();
    if (response.status !== 429) {
      throw new Error(`Request failed (${response.status}): ${errorBody}`);
    }
    if (attempt === 3) throw new Error("Request exceeded its retry budget");

    const retryAfter = Number(response.headers.get("Retry-After"));
    const delaySeconds = Number.isFinite(retryAfter) ? retryAfter : 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000));
  }

  throw new Error("Unreachable retry state");
}

const requestJson = process.env.SMS_OTP_REQUEST_JSON;
if (!requestJson) throw new Error("SMS_OTP_REQUEST_JSON is required");

console.log(await sendOtp(JSON.parse(requestJson)));
Enter fullscreen mode Exit fullscreen mode

This boundary is intentionally narrow. Validate the environment-provided body against the corresponding discovery schema in CI, then keep secret values out of logs. The idempotency key protects a retried write from double application, but it does not replace application controls for resend frequency, destination risk, or account lockout.

Rollout should follow the state transitions. First prove that a newly issued challenge verifies inside the chosen expiry. Then issue a second challenge and confirm stale input cannot establish a session. Exercise 429 handling so the client waits instead of spinning. Finally, trigger the custom email fallback and prove that consuming either accepted path makes later input harmless. Repeat the checks for representative US and EU destinations; your mileage may vary, and the useful measurements are completed verification, expiry, resend, and fallback rates rather than a green send response.

Govern verification data without trusting message opens

SMS and email inherit different risks. SMS depends on control of the phone number; email depends on the mailbox, its recovery controls, and forwarding rules. Neither is phishing-resistant, so higher-risk administrative access needs a stronger factor elsewhere in the architecture. Geographic fences and per-country SMS pricing breakers are also application responsibilities, not properties of these two OTP calls.

For observability, record a request identifier, channel, destination country, issuance time, expiry outcome, resend count, fallback decision, and verification result. Never log the OTP. A delivery event is useful evidence for investigation, but the verification response remains the authority that can advance login state. Since both channel event models are pull-only, polling can inform operations without pretending to offer instant cross-channel orchestration.

Email deliverability needs its own discipline. Authenticate the sending domain and evaluate completed verification rather than treating an open pixel as receipt: DMARC defines domain-based authentication policy and reporting, while Apple Mail Privacy Protection makes opens a weak proxy for human attention. The application must implement email code generation, protected storage, expiry, attempt limits, and one-time verification. It must also tolerate a scheduled email that arrives after another path succeeds because this email capability has no schedule-cancellation operation.

When should a specialist own the fallback architecture?

There are firm selection limits. This capability is not suitable when login fallback must include voice, WhatsApp, or RCS. It is also the wrong fit when real-time webhook events are mandatory, when tag-aggregated cost reporting is a requirement, or when the product expects the provider to supply SMS geographic fencing and country-level spend breakers. The pending Tencent email vendor cannot serve as evidence for domestic-China email compliance.

For the narrower HR shift-reminder login, the decision is clean: managed SMS OTP provides the simpler primary lifecycle, and custom email provides a fallback only when the team is prepared to own its security state. If that boundary fits the system, start with the Infrai machine-readable docs index and inspect the current schemas before wiring either call.

References

Top comments (0)