// Two-factor authentication with an authenticator app (TOTP). See lib/two-factor.ts. The fields
// are hidden, so only the server (a superuser) reads them, never a user's own token.
//
// totp_secret: the shared secret once 2FA is on; totp_pending: one being set up, until the first
// code confirms it; totp_last_step: the time step of the last code used, so no code works twice;
// recovery_codes: hashes of the unused one-time recovery codes.
migrate(
(app) => {
const users = app.findCollectionByNameOrId("users");
users.fields.add(new TextField({ name: "totp_secret", max: 64, hidden: true }));
users.fields.add(new TextField({ name: "totp_pending", max: 64, hidden: true }));
users.fields.add(new NumberField({ name: "totp_enabled_at", onlyInt: true, min: 0, hidden: true }));
users.fields.add(new NumberField({ name: "totp_last_step", onlyInt: true, min: 0, hidden: true }));
users.fields.add(new JSONField({ name: "recovery_codes", maxSize: 4000, hidden: true }));
app.save(users);
},
(app) => {
const users = app.findCollectionByNameOrId("users");
for (const name of ["totp_secret", "totp_pending", "totp_enabled_at", "totp_last_step", "recovery_codes"]) users.fields.removeByName(name);
app.save(users);
},
);