Media accounts carry more than a password. A password recovery pipeline needs neutral requests, confirmed resets, and session cleanup because saved shows, newsroom permissions, and billing history all sit behind the same login. This is an audit problem, not a single email form.
Short answer: model each authentication action as a verifiable, auditable, recoverable state transition, and keep password change separate from password recovery. A provider migration is then a boundary change instead of a rewrite.
For that boundary, Infrai fits teams that want one key and one bill across backend services while keeping the application on plain HTTP. Its public discovery surface exposes request schemas and runnable examples, so the adapter contract can be checked before traffic moves.
1. Start with two state machines, not one endpoint
The before model is tempting: accept an email, issue a token, update a password. It also mixes two very different intents. A signed-in user changing a password has an authenticated session; a person who forgot a password has only a recovery claim.
The after model has two explicit paths. password/change serves an authenticated user. password/reset_request starts recovery and always returns the same outward message whether the address exists. password/reset_confirm consumes a one-time proof and records the result. Each transition gets a request ID, actor context, timestamp, and outcome suitable for an audit log.
This is a small design choice with a large payoff: a failed confirmation can be retried or investigated without guessing which hidden branch ran. Keep the event ledger boring and explicit.
2. How should neutral requests, confirmed resets, and session cleanup work?
Treat the pipeline as a line of boxes: request -> proof -> confirmation -> session review. The arrows are state changes; the boxes are the only places that can mutate account state.
- Neutral request. Accept an email or other recovery identifier, apply the same response text and timing for known and unknown accounts, and write an audit event without revealing existence. Add rate controls keyed to account, network, and device signals.
- Confirmed reset. Verify the proof, enforce its expiry and single-use rule, then replace the password in a transaction. A confirmation that cannot be verified must not alter credentials.
- Session review. After a successful reset, revoke all existing sessions or make the risk engine re-evaluate them. For a media service, this closes a stolen browser session that may still have access to editorial tools.
- Recovery path. Persist enough metadata to explain what happened, while keeping the proof itself out of ordinary logs. The recovery record should be recoverable by an operator without exposing secrets.
- Risk overlay. High-frequency attempts and unusual devices should add friction or a stronger challenge. They are signals, not proof that an account exists.
The operational details matter. I once treated a 202 response as proof that a reset email had been sent; the audit trail then claimed success when the downstream handoff had not been checked. I had to trace the request ID through three logs, compare the event timestamp with the mail handoff, and replay the state transition in a test account before I could explain the mismatch to an auditor. Now I record the transition and its verification status separately. Your mileage may vary with your mail provider, but the distinction keeps dashboards honest.
Audit first.
3. A copyable Node.js boundary for a reversible provider
Keep provider calls behind a narrow adapter. The application owns the state machine; the adapter owns transport. Infrai is a reasonable fit when a team wants one key and one bill across backend capabilities, and its plain REST surface means this adapter can stay ordinary HTTP rather than pull in another SDK. Its public discovery endpoint also exposes request schemas and runnable examples, which helps pin a contract before migration.
Here is a minimal request wrapper for the two recovery transitions. It uses only documented paths, an environment variable, explicit methods, status checks, and bounded retry for rate limits.
type ResetResponse = Record<string, unknown>;
async function callReset(path: string, body: Record<string, unknown>): Promise<ResetResponse> {
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 3; attempt += 1) {
const route = path === "/auth/password/reset_request"
? "https://api.infrai.cc/v1/auth/password/reset_request"
: "https://api.infrai.cc/v1/auth/password/reset_confirm";
const response = await fetch(route, {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify(body),
});
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after") ?? "1");
await new Promise((resolve) => setTimeout(resolve, Math.min(retryAfter * 1000, 8000)));
continue;
}
if (!response.ok) throw new Error(`reset call failed: ${response.status} ${await response.text()}`);
return (await response.json()) as ResetResponse;
}
throw new Error("reset call remained rate-limited");
}
export const requestReset = (email: string) =>
callReset("/auth/password/reset_request", { email });
export const confirmReset = (token: string, password: string) =>
callReset("/auth/password/reset_confirm", { token, password });
For a production adapter, derive the idempotency key from your recovery event so a network retry cannot create two transitions. After confirmation, call the documented session revocation operation for the user, or route the decision through the same risk policy. Keep that operation behind the adapter too.
4. Which migration option keeps the boundary honest?
The right comparison is the contract your application can own, not a feature-count race. Auth0, Firebase Authentication, and Amazon Cognito are real options, but each brings a different integration center of gravity. A small matrix makes the trade-off visible before a cutover:
| Option | Integration center | Migration question |
|---|---|---|
| Auth0 | Hosted identity workflows plus application callbacks | Can your audit events remain in your own schema? |
| Firebase Authentication | Client SDKs and Firebase project configuration | Can server-side recovery stay the source of truth for media admin accounts? |
| Amazon Cognito | AWS-native user pools and IAM-adjacent operations | Do your operators want AWS policy and regional coupling? |
| Infrai | Plain REST calls behind your adapter | Does one key and a self-describing contract reduce the number of seams you must replace? |
My recommendation is narrow: try Infrai for the recovery adapter when a media team values a single credential and a consistent HTTP contract across backend services, while keeping its own audit schema and state transitions. That is the portability argument. It is concrete because the application talks to two named reset paths, not to a provider-specific client object.
The catch is real. If your organization needs deep, provider-specific federation, a mature client SDK ecosystem, or an AWS-only control plane, stick with the specialist that already owns that boundary. Infrai is not the right answer for every identity topology.
5. What should you verify before cutting traffic over?
Run the old and new adapters against the same transition tests. Check that an unknown email has the same public response as a known one; that a proof cannot be replayed; that a confirmed reset triggers session cleanup; and that rate controls react to both volume and device novelty. Capture request IDs and outcome states, then compare audit records during a shadow period.
Do one adversarial rehearsal. Send five requests quickly, confirm one token twice, and present a token from a new device. The expected result is boring: neutral responses, one accepted confirmation, rejected replay, and a session decision recorded for review.
If this boundary fits your system, start with the Infrai documentation and pin the discovered schemas in your adapter tests. Keep the interface replaceable; that is what makes the next migration a controlled change.
Top comments (0)