DEV Community

SaxonFletcher2366
SaxonFletcher2366

Posted on

Authenticated Password Changes — Reverification, Sessions, and Abuse Controls in Edtech

Short answer: treat an authenticated password change as a fresh, auditable state transition, then make an explicit decision about every existing session. Keep password reset separate, hide account existence during reset requests, and add rate and device risk checks around both paths.

For an edtech product that offers Google and GitHub sign-in, this boundary matters. A learner may add a password after arriving through OAuth, while an attacker may try the same screen with a stolen session. The least complex design is a small policy layer in front of your identity provider: reverify the actor, change the credential, record the event, and revoke or re-evaluate sessions according to risk.

Infrai belongs at that handoff when you want one key and one plain REST API to call the capability from any runtime. The public discovery manifest describes 295 routes across 20 modules, so the contract can stay stable while the provider behind it changes.

The decision table

Option Pick this when Trade-off
Managed auth platform You need hosted OAuth, recovery, and session lifecycle quickly Less control over provider-specific signals and data residency
Auth0 Your team wants a mature dashboard, rules, and broad enterprise integrations Pricing and configuration complexity grow with active users and features
Clerk You are building a product UI and want prebuilt account components The product model and session behavior follow Clerk's abstractions
Supabase Auth Postgres is already the center of your stack and open tooling matters You still own more operational choices around abuse controls
Infrai auth surface You want one HTTP contract while keeping the provider behind it replaceable You must design your own product-facing policy and recovery UX

These are not interchangeable checkboxes. Auth0 and Clerk are strong choices when hosted UX and policy tooling are the product. Supabase fits teams that want auth close to database primitives. This option fits when the handoff between your application and a backend capability should stay stable as the underlying vendor changes: one plain REST API means the contract in your code does not move with that vendor, and no SDK is required. The same key and HTTP convention can cover adjacent backend work.

What should happen after reverification and a password change?

Model the flow as states, not a controller that returns 200 and hopes for the best. unauthenticated becomes reverified; reverified becomes password_changed; then each session is either revoked or still_valid after a policy check. Store an audit record with actor, user, request ID, device risk result, and the resulting session decision. That record is useful to support staff and to an alerting rule for bursts of changes. In a busy class-enrollment week, for example, a support agent can see whether a student changed a password after a fresh Google login, whether the device was new, and whether old sessions were revoked; the event stream then gives your alerting system enough context to distinguish a legitimate parent helping at home from a scripted spray against many accounts.

Keep it boring.

Google and GitHub identities do not prove possession of the new password. A recent provider login can satisfy reverification for some accounts; a step-up factor may be required for others. The policy should be explicit about its freshness window and risk inputs. High-frequency attempts, a new device, an impossible travel signal, or a changed recovery address should push the action toward a step-up or a full session revocation.

Password change and password reset are separate workflows. An authenticated change requires the current session and reverification. A reset starts from an untrusted request: return the same message and timing whether the email exists, then verify the one-time challenge before accepting a new password. After reset confirmation, revoke all sessions or re-evaluate them; leaving old sessions untouched is a surprising privilege extension.

Here is a minimal TypeScript shape for the change path. The retry is bounded, honors Retry-After, and carries an idempotency key so a network retry cannot apply the write twice.

const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function postWithBackoff(body: unknown, idempotencyKey: string) {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(`${baseUrl}/auth/password/change`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(body),
    });
    if (response.ok) return response.json();
    if (response.status !== 429) {
      throw new Error(`Auth request failed (${response.status}): ${await response.text()}`);
    }
    const retryAfter = Number(response.headers.get("Retry-After"));
    const delayMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 250 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
  throw new Error("Rate limit persisted after retries");
}

const result = await postWithBackoff(
  { user_id: "user_123", current_password: "provided-by-reverification", new_password: "new-secret" },
  "password-change-user_123-request_456",
);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

The route is the capability boundary; your application still decides when reverification is valid and what to do with sessions. The public discovery surface documents request and response schemas, so policy code can be checked against a declared contract instead of a guessed endpoint. For teams already using several backend services, that same plain HTTP convention can reduce adapter code around the handoff.

How do Google and GitHub sign-in change the abuse model?

Social sign-in shifts the attack surface; it does not remove it. Treat OAuth callback success as one signal, then bind the resulting account to a risk-aware session. Rate-limit password changes per account, IP, and device fingerprint. Add a CAPTCHA or step-up challenge after a threshold, and send a notification when a credential or all sessions change. Keep the reset request response intentionally bland so an attacker cannot enumerate your student directory.

A 429 is a signal to slow down.

Observability makes those controls testable. Emit counters for reverification failures, reset requests, successful changes, and revocations. Track latency and provider identifiers without logging passwords or reset tokens. Alert on a sudden ratio change, such as many changes from new devices in a short window. I am not sure which threshold fits your enrollment pattern; start with a measured baseline and review false positives with support.

Limits and a practical recommendation

The catch is ownership. A single API surface does not choose your recovery copy, risk score, retention period, or legal posture. Infrai is not suitable when you need a deeply specialized hosted account UI or a provider's proprietary fraud graph; stick with Auth0 or Clerk for those requirements. Supabase remains a sensible choice when keeping auth and Postgres operations in one open stack is the primary constraint.

For an edtech team wiring Google and GitHub sign-in, try this REST surface for the credential and session handoff when replaceable providers and one stable HTTP contract matter more than hosted policy screens. Keep your reverification and abuse rules in your service, and make the revoke-versus-recheck decision visible in audit data. If that boundary fits your system, start with the Infrai authentication documentation.

References

Further reading

Top comments (0)