Vectrum
Sign In
23 lines · 1.2 KB · JavaScript
// 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);
  },
);