Two-factor authentication (OTP)
Contember supports TOTP-based two-factor authentication. Once enabled for a person, every sign-in (signIn and signInPasswordless) requires the current OTP code in addition to the regular credentials.
All three OTP mutations are self-service — they operate on the calling person. An administrator cannot enable or disable 2FA on someone else's behalf; the only admin lever is disablePerson + forceSignOutPerson to lock the account out entirely.
Enabling 2FA
The setup is a two-step exchange:
1. Prepare
mutation {
prepareOtp(label: "Contember Admin") {
ok
result {
otpUri
otpSecret
}
}
}
labelis the human-readable name the authenticator app will show. Defaults to"Contember".otpUriis aotpauth://URI — render it as a QR code for the user.otpSecretis the raw shared secret, for users who prefer to type it manually.
prepareOtp is idempotent — calling it again rotates the secret. If the person already had 2FA enabled, an audit 2fa_disable entry is written before the new pair is issued (because the previous secret is now invalid).
2. Confirm
mutation {
confirmOtp(otpToken: "123456") {
ok
error { code developerMessage }
}
}
The client asks the user to read the current code from their authenticator app and submits it. The token is validated against the secret created by prepareOtp. On success, 2FA is activated for the person.
Errors:
| Code | Cause |
|---|---|
NOT_PREPARED | prepareOtp was not called for this person. |
INVALID_OTP_TOKEN | The submitted code does not match. |
A successful confirm records 2fa_enable in the audit log; a rejected one records the failure too.
Disabling 2FA
mutation {
disableOtp {
ok
error { code }
}
}
Errors:
| Code | Cause |
|---|---|
OTP_NOT_ACTIVE | 2FA is not enabled for this person. |
Records 2fa_disable in the audit log.
Sign-in with 2FA enabled
signIn and signInPasswordless accept an otpToken argument. When 2FA is enabled and the token is missing or wrong:
| Mutation | Missing token | Wrong token |
|---|---|---|
signIn | OTP_REQUIRED | INVALID_OTP_TOKEN |
signInPasswordless | OTP_REQUIRED | INVALID_OTP_TOKEN |
Clients should react to OTP_REQUIRED by prompting for the code and retrying the same mutation with otpToken populated.
Where 2FA does not apply
signInIDP— the identity provider is responsible for second-factor enforcement. Contember does not re-check OTP after a successful IDP callback.createSessionToken— admin impersonation bypasses 2FA by design.- Permanent API keys — 2FA only protects person logins; permanent keys are credentials in their own right.