Two-Factor Authentication
Your Zenavo account can reach every server you connected, so protect it with a second factor. Turn it on under Account settings → Security by setting up an authenticator app, adding a passkey, or both.
Authenticator app
- Click Set up and scan the QR code with an authenticator app (1Password, Google Authenticator, Microsoft Authenticator, Authy or any app that supports TOTP). If you can't scan, type the setup key instead.
- Enter the 6-digit code the app shows.
- Save the 10 recovery codes somewhere safe, such as a password manager. They are shown only once.
Your first second factor, the app or a passkey, turns two-factor authentication on: it creates the recovery codes and signs out your other sessions, since they were signed in without it. Adding the other one later keeps your recovery codes.
Passkeys
Passkeys work on their own or next to the authenticator app: you use one to finish signing in and to confirm sensitive actions. A passkey lives in Touch ID or Face ID (iCloud Keychain), Windows Hello, Google Password Manager, a password manager like 1Password, or a hardware security key such as a YubiKey.
- Under Security → Passkeys, click Add passkey.
- Give it a name you will recognise, like "MacBook Touch ID", and click Continue.
- Follow your browser's prompt.
- If this is your first second factor, save the recovery codes shown next.
You can add up to 20, for example one per device. Each shows when it was added and last used, and whether it syncs between your devices. Remove deletes one. Adding or removing a passkey asks you to confirm it's you first.
Passkeys are tied to your control plane's address (ZENAVO_PUBLIC_URL); they work only when you open the console there. A self-hosted control plane needs ZENAVO_PUBLIC_URL set to offer them, except on localhost for local development.
An account with only passkeys cannot confirm sensitive actions from the CLI or with an API key, since a passkey works only in the browser. Do those in the console, or also set up the authenticator app.
Turn off on the authenticator app removes only the app; your passkeys and recovery codes keep working. Two-factor authentication turns off, and the recovery codes are deleted, once neither the app nor a passkey is left.
Signing in
After your password, or after GitHub or Google, Zenavo asks for the current code from your app, or Use a passkey if you added one. Each code works once. If you lost your phone, choose Use a recovery code: each recovery code also works once. With only passkeys, the fallback is a recovery code. Security shows how many are left; Generate new codes replaces them all.
A password reset by email changes your password but not the second factor: you still need a code to sign in afterwards.
Five wrong codes lock code entry for that account for 15 minutes.
Confirming sensitive actions
Some actions ask you to confirm it's you, even when you are signed in:
- creating an API key, and approving a CLI sign-in
- changing your email or password, and deleting your account
- turning the authenticator app off, generating new recovery codes, and adding or removing a passkey
- adding a server
- transferring or deleting a team
With two-factor authentication on, you confirm with a code from the app, a recovery code, or a passkey, whichever you have set up. Without it, you confirm with your password; accounts that sign in only with GitHub or Google sign in again. A confirmation, like signing in, lasts five minutes.
API keys have no session to confirm. For an account with two-factor authentication, send the current code from the authenticator app with these requests in the X-Zenavo-OTP header. Passkeys cannot be used here, so an account with only passkeys gets 403 and does these actions in the console:
curl -X POST -H "Authorization: Bearer $ZENAVO_API_KEY" -H "X-Zenavo-OTP: 123456" \
-H "Content-Type: application/json" -d '{"confirmation": "My Team"}' \
https://zenavo.app/api/teams/$TEAM/deleteNew sign-in emails
Zenavo emails you when someone gets lasting access to your account:
- a sign-in (password, GitHub, Google or a password reset link) from a browser your account has not used before, sent once the second factor is accepted when two-factor authentication is on
- a CLI sign-in you approved
- a new API key
The email shows the time, the approximate browser and system (from the browser's user agent), the IP address and the sign-in method, with a link to Account settings → Sessions to sign that device out. It never contains tokens, codes or secrets. If it wasn't you, sign the session out (or delete the key), change your password and turn on two-factor authentication.
Zenavo recognises a browser by a long-lived, random cookie it leaves at sign-in; your account keeps only a hash of it. A browser without the cookie (cleared, private window) still counts as known when the same browser and system signs in from the same network (the IP's /24, or /48 for IPv6) as a device or session you used recently. Devices unused for 180 days are forgotten. The first sign-in right after you sign up sends nothing. On a control plane that just added this feature, your first sign-in from each browser may send one email, since no browser is known yet.
You get at most one of these an hour per device. Each one is recorded in the audit log. They are on for everyone; you can turn them off under Account settings → Notifications, but we don't recommend it.
Recent activity
Security also lists your recent sign-ins, failed attempts, API keys and other account changes, from the audit log. Check it if you suspect someone else used your account.
How it is stored
The authenticator secret is encrypted with the control plane's master key. Recovery codes are kept only as hashes.
For a passkey, Zenavo keeps only its public key, credential ID, the authenticator model (AAGUID), the name you gave it and a signature counter; the private key never leaves your device. Every passkey prompt answers a fresh challenge that works once, expires after five minutes and is bound to your session (or, while signing in, to that sign-in). If a passkey's signature counter goes backwards, a sign of a copied key, Zenavo refuses it and records that in the audit log; remove that passkey.