Per-workspace MFA enforcement helpers (SEC-004).
Workspaces with mfaRequired = 1 (migration 028) block login for any
member whose user account has mfaEnabled = 0 once they are past the
grace window. The grace window starts at the later of:
- the workspace's
mfaPolicyUpdatedAt(when the policy flipped on) - the membership's
joinedAt(so a new hire still gets a fair window even if the policy has been in place for months) and lastsmfaGracePeriodDayscalendar days.
A user who belongs to multiple workspaces is enforced by the strictest one: if ANY workspace they belong to is past-grace and requires MFA, login is blocked.
Exports
evaluateMfaEnforcement— Decide enforce / grace / allow for a user.
- Source:
Methods
(static) evaluateMfaEnforcement(user, optsopt) → {MfaEnforcementDecision}
Evaluate whether MFA enforcement blocks, grace-warns, or allows a login
for a user. Pure function — no side effects, safe to call from any auth
surface (/login, /refresh, OAuth callbacks).
Users are "MFA-enabled" if EITHER mfaEnabled === 1 (TOTP) OR they have at
least one registered WebAuthn credential. Either factor satisfies the
workspace requirement — otherwise an OAuth-only user with a passkey but no
TOTP would be falsely blocked at OAuth callback time.
Parameters:
| Name | Type | Attributes | Description | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
user |
Object | User row (must include |
|||||||||||
opts |
Object |
<optional> |
Properties
|
- Source:
Returns:
- Type
- MfaEnforcementDecision
Type Definitions
MfaEnforcementDecision
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
state |
"allow" | "grace" | "block" | Final decision. |
|
workspaceId |
string |
<optional> |
Workspace that produced the decision (block / grace only). |
workspaceName |
string |
<optional> |
Display name for the same workspace. |
gracePeriodDaysRemaining |
number |
<optional> |
Ceil-rounded days left when state="grace". |
graceEndsAt |
string |
<optional> |
ISO timestamp when the user becomes blocked. |
- Source: