Reauthentication
Reauthentication asks a signed-in user to verify an authentication factor again before a sensitive action. After the factor succeeds, Forte updates lastReauthenticatedAt on the current session. Your app decides how recent that timestamp must be before it continues.
Reauthentication uses Forte's client-side API. A Forte Service frontend can use the first-party /_forte path. Mobile apps and server-side code must pass the user's session token as a bearer token. Authenticate these calls with the user's session, not FORTE_API_TOKEN.
How reauthentication works
- Call
getReauthenticationStatusto readlastReauthenticatedAtandavailableFactors. - If the user chooses email or SMS, call
createReauthenticationChallengeto send a one-time code. Passwords, authenticator codes, and backup codes do not need a challenge. - Call
completeReauthenticationwith the selected factor and credential. - Before the sensitive action runs, call
getReauthenticationStatusfrom your backend. Continue only whenlastReauthenticatedAtfalls within your freshness window.
Reauthentication does not create or replace a session token. It updates only the current session, and listSessions also returns the timestamp.
Choose an available factor
availableFactors follows this order:
- If the user has a verified authenticator app or unused backup codes, Forte returns only those factors.
- Otherwise, Forte returns the user's password when password sign-in is enabled and the user has a password.
- Forte also returns each verified email or phone that can receive a one-time code through an enabled sign-in channel.
Each email or SMS factor includes a contactMethodId and a masked destination such as a***@example.com. Show each destination as a separate choice. Pass its contactMethodId as targetContactMethodId when you create the challenge.
An impersonation session has no available factors. Passkeys and Google reauthentication are not supported.
Reauthenticate the user
This example uses an email one-time code from a Forte Service frontend. The /_forte path keeps the session cookie first-party.
import { ForteClient } from "@forteplatforms/sdk";
const forte = new ForteClient({ baseUrl: "/_forte" });
const status = await forte.users.getReauthenticationStatus({ projectId });
const emailFactor = status.availableFactors.find(
(factor) => factor.type === "EMAIL_OTP",
);
if (!emailFactor) {
throw new Error("Email reauthentication is not available");
}
await forte.users.createReauthenticationChallenge({
projectId,
reauthenticationChallengeRequest: {
method: "EMAIL_OTP",
targetContactMethodId: emailFactor.contactMethodId,
},
});
const result = await forte.users.completeReauthentication({
projectId,
reauthenticationRequest: {
method: "EMAIL_OTP",
credential: codeEnteredByUser,
},
});
console.log(result.reauthenticatedAt);For a password, authenticator code (TOTP), or backup code (BACKUP_CODE), skip the challenge. Pass the credential directly to completeReauthentication.
In a sandbox project, a contact method with a fixed test code uses that code for reauthentication. Forte does not send an email or SMS.
Enforce a freshness window
Enforce the window on the backend that performs the sensitive action. Pass the user's session token to Forte, then compare lastReauthenticatedAt with your cutoff. Do not accept a timestamp from the browser.
const status = await forte.users.getReauthenticationStatus({
projectId,
authorization: `Bearer ${sessionToken}`,
});
const cutoff = Date.now() - 5 * 60 * 1000;
const isFresh =
status.lastReauthenticatedAt != null &&
new Date(status.lastReauthenticatedAt).getTime() >= cutoff;
if (!isFresh) {
return respondNeedsReauthentication();
}
performSensitiveAction();The timestamp applies to the whole session, not to one action. Any action using the same freshness window can accept the same reauthentication.
Handle errors
| Code | HTTP | Cause |
|---|---|---|
REAUTHENTICATION_FACTOR_NOT_AVAILABLE | 400 | The factor is not available for this user, project, or session. |
REAUTHENTICATION_INVALID_CREDENTIAL | 400 | The password, authenticator code, one-time code, or backup code is incorrect. |
REAUTHENTICATION_CODE_EXPIRED | 400 | The email or SMS code expired or reached the attempt limit. |
REAUTHENTICATION_NO_ACTIVE_CHALLENGE | 400 | No email or SMS challenge is active. Create a challenge first. |
REAUTHENTICATION_RATE_LIMITED | 429 | The user requested another code before the resend delay ended. |
MFA_REQUIRED | 401 | Sign-in MFA is still pending. Complete MFA before reauthentication. |
Next steps
- Configure sign-in factors in Authentication
- Add a second factor with Multi-factor authentication
- Review token behavior in Sessions