MFA and user impersonation
Apply multi-factor authentication and use superuser-only impersonation with explicit security boundaries.
PocketBase supports multi-factor authentication (MFA) for auth collections and a separate impersonation endpoint for superusers. MFA requires two different authentication methods; impersonation creates a temporary, nonrenewable token for acting as another auth record.
Security model
MFA is an end-user authentication flow. Impersonation is an administrative capability and must be restricted to controlled, server-side operations.
MFA reference
Enable MFA
Enable the Multi-factor authentication option for the target auth collection. The exact enrollment and challenge payloads belong to the authentication API surface; use the collection's configured methods and the matching SDK method for the second factor.
Complete the two-factor exchange
- Authenticate with the first method, such as password.
- When MFA is required, handle the
401response and read itsmfaIdvalue. - Authenticate with a different method and include that
mfaIdin the request body or query parameters. - Treat the returned token and auth record as the completed authentication.
try {
await pb.collection("users").authWithPassword("person@example.com", "password-from-user");
} catch (error) {
const mfaId = error.response?.mfaId;
if (!mfaId) throw error;
const otp = await pb.collection("users").requestOTP("person@example.com");
await pb.collection("users").authWithOTP(otp.otpId, "CODE_FROM_EMAIL", { mfaId });
}Password changes
Changing a user's password clears that user's MFA state. Require the user to enroll or complete MFA again according to your application policy after a password change.
Impersonation reference
POST /api/collections/{collection}/impersonate/{id}
Use this operation only with a superuser authorization token. {collection} is the auth collection and {id} is the target auth record. The route is protected by superuser authentication.
await pb.collection("_superusers").authWithPassword(
"admin@example.com",
"superuser-password-from-secret-store",
);
const impersonated = await pb.collection("users").impersonate("USER_RECORD_ID", 3600);
const records = await impersonated.collection("orders").getFullList();The optional duration is expressed in seconds. The returned client keeps the impersonation token in memory, and the token cannot be renewed.
Invalidate impersonation access
Because impersonation tokens are not renewable, use a bounded duration and discard the client after the operation. If already issued superuser-derived tokens must be invalidated, change the relevant superuser password or rotate the shared superuser auth-token secret according to your operational procedure.
Verify
For MFA, verify that the first method returns mfaId and that the second method returns a normal auth response. For impersonation, verify that the generated client can perform only the intended server-side operation, then discard it and its in-memory token.
Next step
Use API rules and filters to define the access boundaries that ordinary authenticated users must follow.