Two-step verification
Require a code from an authenticator app when you sign in, keep recovery codes for a lost phone, and know what to do when something goes wrong.
On this page
Two-step verification (also called two-factor authentication, or 2FA) asks for a six-digit code from an app on your phone every time you sign in, in addition to your password or your Google or GitHub sign-in. Someone who learns your password still cannot get into your account. This page shows how to turn it on, how sign-in changes, and how to recover if you lose your phone.
Two-step verification is set per person, not per workspace. Turning it on protects your account in every workspace you belong to.
Before you start#
Install an authenticator app that supports time-based one-time passwords (TOTP), such as 1Password, Authy, Google Authenticator, or Microsoft Authenticator. Make sure your phone's clock is set automatically; codes depend on the time.
Turn it on#
Open the security settings
In the dashboard, open Settings > Security. Under Two-step verification, choose Turn on.
Add SteadyLink to your authenticator app
The Set up your authenticator app dialog shows a QR code. Scan it with your app. If you cannot scan it, add an account in your app manually and enter the setup key shown under the QR code. The account appears in your app as SteadyLink with your email address.
Confirm with a code
Enter the six-digit code your app shows and choose Turn on. If the code does not match, check that your phone's time is correct and try the newest code.
Save your recovery codes
SteadyLink shows ten recovery codes. This is the only time they are shown. Choose Copy or Download, store them somewhere safe and separate from your phone, such as a password manager, then choose I saved them.
Until you confirm the first code, two-step verification is not on. If you close the dialog early, start again; a new setup key is generated each time.
Signing in with two-step verification#
After your password, or after you return from Google or GitHub, SteadyLink asks for the code from your app:
- Open your authenticator app and enter the current six-digit code for SteadyLink.
- If you do not have your phone, choose Use a recovery code and enter one of your saved codes.
A few details that explain common errors:
- Codes change every 30 seconds. SteadyLink accepts the current code and the ones just before and after it, to allow for small clock differences.
- Each code works once. If you sign in twice within the same 30 seconds, wait for the next code.
- You have five minutes to enter the code after your password. After that, sign in again from the start.
- After ten wrong codes in ten minutes, further attempts on your account are refused for a while, even with the right code. Wait and try again.
Single sign-on is different: if your workspace uses single sign-on, signing in through your organization's identity provider does not ask for a SteadyLink code. Your identity provider is responsible for the second factor there.
Recovery codes#
Recovery codes look like k7mp-x3qa-9fhw: twelve letters and digits in three groups. Letters that are easy to confuse, such as i, l, and o, are never used. When you type one, capitals, spaces, and dashes do not matter.
- Each recovery code works once. After you use one, it is gone.
- Settings > Security shows how many codes you have left, and warns you when you are running low.
- To get a fresh set, choose New recovery codes and confirm with your password, a current code from your app, or one of your unused recovery codes. Your old codes stop working immediately.
If you lose your phone#
Sign in with a recovery code
At the code prompt, choose Use a recovery code and enter one of your saved codes.
Move two-step verification to your new phone
In Settings > Security, choose Turn off and confirm with your password or another unused recovery code. Then choose Turn on again and scan the new QR code with your new phone. The old phone's codes stop working as soon as you turn it off.
Save the new recovery codes
Turning it on again creates ten new recovery codes. Store them, and discard the old ones.
If you have lost both your phone and your recovery codes, you cannot sign in on your own. Contact [email protected] from the email address on the account.
Turn it off#
In Settings > Security, choose Turn off and confirm in the same field with your password, a current code from your app, or an unused recovery code. If your account has no password because you sign in with Google or GitHub, use a code. Turning it off deletes your setup key and all recovery codes.
What it does not cover#
- API keys and S3 app keys are not affected. They are separate credentials for integrations and never ask for a code. Protect them as described in API key safety.
- Browsers already signed in stay signed in. To end other sessions, use Signed-in browsers in Settings > Security.
- Workspaces cannot currently require every member to turn on two-step verification. To require a second factor for everyone, use single sign-on with an identity provider that enforces it.
For developers#
The dashboard uses these endpoints under /api/auth/2fa. All of them need a signed-in user session; API keys cannot call them.
| Route | Purpose |
|---|---|
GET /api/auth/2fa | Status: enabled, enabledAt, pending, and recoveryCodesRemaining. |
POST /api/auth/2fa/enroll | Starts setup. Returns secret, otpauthUri (render it as a QR code), digits (6), and period (30). |
POST /api/auth/2fa/confirm | Body { "code": "123456" }. Turns it on and returns recoveryCodes once. |
POST /api/auth/2fa/recovery-codes | Replaces recovery codes. Body: password, code, or recovery_code. |
POST /api/auth/2fa/disable | Turns it off. Body: password, code, or recovery_code. |
POST /api/auth/2fa/verify | Completes a sign-in. Body: challenge_token with code or recovery_code. |
When an account has two-step verification on, a password sign-in returns { "mfa_required": true, "mfa_token": "...", "expires_in": 300 } instead of a session. Send the mfa_token as challenge_token to /verify with the code. Errors use stable codes such as invalid_code, mfa_challenge_expired, and mfa_attempt_limit_exceeded.