Skip to content

Multi-Factor Authentication

Add an extra layer of security to your account with Multi-Factor Authentication (MFA).


MethodTypeDescription
Authenticator ApptotpTime-based codes from apps like Google Authenticator, Authy
EmailemailVerification codes sent to your email

To enable MFA on your account, first enroll a factor, then verify it.

Starts enrollment of a new MFA factor. Requires JWT authentication.

Request:

{
"type": "totp"
}
FieldTypeDescription
typestringFactor type: totp or email

Response (TOTP):

{
"factorId": "fac_abc123",
"type": "totp",
"qrCode": "data:image/png;base64,iVBORw0KGgo...",
"secret": "JBSWY3DPEHPK3PXP",
"challengeId": "chl_abc123"
}
FieldTypeDescription
factorIdstringThe new factor ID
typestringFactor type
qrCodestringQR code image (data URL) for authenticator apps
secretstringManual entry code if QR scanning unavailable
challengeIdstringChallenge ID for verification step

Completes MFA enrollment by verifying the first code.

Request:

{
"challengeId": "chl_abc123",
"code": "123456"
}
FieldTypeRequiredDescription
challengeIdstringYesChallenge ID from enrollment
codestringYes6-digit code from authenticator app

Response:

{
"success": true,
"message": "MFA factor enrolled successfully"
}

Lists all enrolled MFA factors. Requires JWT authentication.

Response:

{
"factors": [
{
"id": "fac_abc123",
"type": "totp",
"createdAt": "2024-01-15T10:00:00Z"
},
{
"id": "fac_def456",
"type": "email",
"createdAt": "2024-01-10T08:00:00Z"
}
]
}

Removes an MFA factor. Requires JWT authentication.

Response: 204 No Content


When MFA is enabled and you log in, you’ll receive an MfaRequired status with available factors.

{
"status": "MfaRequired",
"pendingInfo": {
"pendingAuthenticationToken": "pat_abc123...",
"availableFactors": [
{ "id": "fac_abc123", "type": "totp" },
{ "id": "fac_def456", "type": "email" }
],
"challenge": {
"id": "chl_abc123",
"factorId": "fac_abc123",
"expiresAt": "2024-01-15T10:05:00Z"
}
}
}

Creates a new MFA challenge for a specific factor.

Request:

{
"factorId": "fac_abc123"
}

Response:

{
"challengeId": "chl_xyz789",
"expiresAt": "2024-01-15T10:05:00Z"
}

Verifies the MFA code during login to complete authentication.

Request:

{
"pendingAuthenticationToken": "pat_abc123...",
"challengeId": "chl_xyz789",
"code": "123456"
}
FieldTypeRequiredDescription
pendingAuthenticationTokenstringYesToken from login response
challengeIdstringYesChallenge ID
codestringYes6-digit verification code

Response:

{
"accessToken": "eyJhbGci...",
"refreshToken": "rt_abc123...",
"expiresIn": 3600,
"userId": "usr_abc123",
"email": "john@example.com",
"name": "John Doe",
"avatarUrl": null,
"personalOrgId": "org_abc123",
"permissions": ["database:read", "database:write"],
"roles": ["member"]
}

// 1. Attempt login
var loginResult = await client.Auth.LoginWithPasswordAsync(
new PasswordLoginRequest(
Email: "john@example.com",
Password: "SecurePassword123!"
)
);
if (loginResult.Value.Status == PasswordLoginStatus.MfaRequired)
{
var pendingInfo = loginResult.Value.PendingInfo!;
// 2. Get the code from user's authenticator app
Console.Write("Enter MFA code: ");
var code = Console.ReadLine();
// 3. Verify MFA
var mfaResult = await client.Auth.VerifyMfaAsync(
new MfaVerifyRequest(
PendingAuthenticationToken: pendingInfo.PendingAuthenticationToken,
ChallengeId: pendingInfo.Challenge!.Id,
Code: code
)
);
if (mfaResult.IsSuccess)
{
client.SetAccessToken(mfaResult.Value.AccessToken);
Console.WriteLine("Login successful!");
}
}