TOTP (Authenticator Apps)
Time-based One-Time Passwords (TOTP) allow users to authenticate using 6-digit codes generated by apps like Google Authenticator, Authy, or 1Password.
Enabling TOTP
Section titled “Enabling TOTP”To use TOTP, enable the totp feature in your Cargo.toml:
[dependencies]authkestra-engine = { version = "0.9", features = ["totp"] }Configuration
Section titled “Configuration”In Authkestra, TOTP is implemented via the TotpAuthMethod struct. You initialize it by passing a CredentialStore where the secrets will be securely saved.
Because a 6-digit TOTP code alone is generally insufficient as a primary credential, it is strongly recommended to register TOTP as an MFA-only method. You do this by registering it with the EngineBuilder using .with_mfa_method().
use authkestra::Authkestra;use authkestra_engine::auth::totp::TotpAuthMethod;
// `my_store` implements the `CredentialStore` trait (e.g. `SqlxCredentialStore`)let engine = Authkestra::builder() .with_mfa_method(TotpAuthMethod::new(my_store)) // Step-up only! .build();By registering it this way, the Engine will automatically block any attempt to use TOTP directly for primary authentication (enforcing that it can only be submitted in an MfaChallenge after a primary method like a password is authenticated).
Registering a Device
Section titled “Registering a Device”To enroll a user, keep a reference to your TotpAuthMethod and call register_totp. It automatically generates a new base32 secret and an otpauth:// provisioning URI. The secret is saved to the store internally.
let (secret_b32, uri) = totp_method.register_totp( "user_123", // user ID "Authkestra", // Issuer (shows in app) "alice@example.com" // Account name).await?;You can convert the returned uri into a QR code using a frontend library (like qrcode.js) for the user to scan.
Authenticating
Section titled “Authenticating”Because TOTP is registered as an MFA-only method, you cannot simply pass AuthInput::Totp to engine.authenticate(). If you try, you will receive an AuthError::Internal explaining that the method is not registered for primary authentication.
Instead, TOTP must be provided in response to an AuthResult::MfaRequired returned by a primary authentication method (such as a password).
Using TOTP as a Second Factor (MFA Continuation)
Section titled “Using TOTP as a Second Factor (MFA Continuation)”If the user logged in with a password and they have TOTP enrolled, the engine will return AuthResult::MfaRequired containing an mfa_token. You complete the login by sending an MfaChallenge:
let result = engine.authenticate(AuthInput::MfaChallenge { mfa_token: previous_mfa_token, challenge_input: Box::new(AuthInput::Totp { user_id: "user_123".to_string(), code: "123456".to_string(), }),}).await?;Built-in Replay Protection
Section titled “Built-in Replay Protection”Authkestra strictly adheres to RFC 6238. It automatically tracks the highest last_used_step in the credential store, preventing the exact same code from being submitted more than once within the validity window. This completely mitigates replay attacks!