Account security
Your account is one identity across every organization you belong to, and the ways you prove it is you are managed in one place: the account menu → Security (/account/security). This chapter covers signing in, the second factor, passkeys, linked sign-in providers, password rules and recovery. Configuring which providers a control plane offers is the operator's side, in Accounts and access.
The model
An account has an email address and up to four ways in:
| Way in | What it is | Second factor? |
|---|---|---|
| Password | Email + password, the way every account starts (except one created by GitHub or Google sign-in). | Yes, when two-factor is on. |
| Two-factor code | A six-digit code from an authenticator app, or one of ten one-time backup codes, asked for after the password. | — it is the second factor. |
| Passkey | A WebAuthn credential (Touch ID, Windows Hello, a security key, a phone) registered from a browser. | No — a passkey sign-in never asks for a code; the passkey is the second factor. |
| Linked provider | A GitHub or Google identity linked to this account. | Yes, when two-factor is on: the code step follows the provider round trip. |
Three rules run through everything below:
- Changing how you sign in needs a fresh proof. Enrolling or disabling two-factor, regenerating backup codes, adding or removing a passkey, and unlinking a provider all require re-authentication. An account that has a password must type it into the Current password field every time — a fresh session does not substitute. An account with no password (it signs in only with a passkey or a linked provider) is let through on a session younger than 15 minutes. Otherwise the request is refused (403) and the app asks for the password. Linking a provider is the one exception: it is a redirect and cannot carry a password, so it always uses the 15-minute window.
- Securing the account signs everyone else out. Each of those changes revokes every other session on the account and keeps only the one that made the change. A password reset revokes all sessions, including the one at the keyboard.
- You cannot remove your last way in. Unlinking the only provider on an account that has no password and no passkey is refused (
last_sign_in_method). Keep at least two ways in before removing one.
Sessions last 7 days and are cookie-bound to the control plane's origin; the native apps use the same session against the control plane you connected to (Change control plane on the sign-in screen).
Sign in
Email and password → Sign In. On a control plane with providers configured, Sign in with GitHub / Sign in with Google sit beside it, and Sign in with a passkey below.
If two-factor is on, the authentication code step follows: six digits from your app, or Use a backup code to type one of the ten. The challenge lives 5 minutes. Five wrong codes within 15 minutes lock two-factor sign-in for that account until the 15 minutes are up; signing in again does not reset the count. Each code works once: a code that already signed you in is refused, even under a new challenge, so wait for the next one from your app.
A new account made by sign-up must verify its email first (the link in the mail is good for 24 hours); until then sign-in is refused with Verify your email before signing in. A control plane without outbound email skips verification entirely.
Forgot your password
On the sign-in screen, Forgot password? sits under the password field.
Enter your account's email and choose Send reset link. The answer is always Check your inbox, whether or not an account uses that address, so the screen never tells a stranger which emails have accounts.
If the address belongs to an active account that has a password, an email arrives with a link that is good for one hour.
The link opens Reset Password: choose a New password (the same rules as everywhere, see Passwords). The page says Your password has been changed and signs you out on every device: sign in again with the new password.
A link that is expired, already used or unknown opens This reset link doesn't work anymore with Request a new link. A control plane that cannot send email cannot send the link, so the email never arrives there.
Turn on two-factor
Security → Two-factor authentication → Enable two-factor.
Enter your current password (re-authentication).
Add this to your authenticator: scan the QR code, or Copy key / Copy link (the base32 secret or the otpauth:// URI) into any TOTP app.
Type the first six-digit Authentication code and Verify and enable. Nothing is enabled until a code verifies.
Save your backup codes. Ten one-time codes, shown once. Copy all, store them where you can reach them without this device, then I've saved these. They are the only way back in if you lose the authenticator.
Afterwards the app shows the method and how many backup codes remain. Regenerate backup codes issues ten new ones and invalidates the old set; Disable two-factor removes the authenticator and the codes. Both re-authenticate.
Add a passkey
Security → Passkeys → Add a passkey. Web only in this release — the native apps show a use a browser note instead of a button, because a passkey must be created on the control plane's own origin.
Enter your current password if asked, and give the passkey a name (the device or authenticator it lives on).
Complete the browser's prompt. User verification is required — a fingerprint, face, device PIN or security-key PIN, not just a touch.
The list shows each passkey's name, when it was added, and where it lives — Synced (a multi-device credential backed up by its provider), Multi-device, or This device only. Remove re-authenticates; a passkey registered twice is refused (passkey_exists).
To sign in with it: Sign in with a passkey on the sign-in screen — no email, no password, no code.
Link GitHub or Google
Security → Linked accounts. Only providers the control plane has configured appear. Linking is web-only (Link from a browser on native). Finish the provider round trip in the same browser that started it: the return trip is refused on any other.
Link next to the provider. Your session must be younger than 15 minutes (otherwise Sign in again to link an account).
Authorize at the provider. You return to Security with Account linked.
TurboPanel stores the provider identity only — no provider tokens. A provider identity already linked to a different user is refused (That provider account is already linked to another user). Unlink re-authenticates and is refused when it would leave no way in.
No linking by email address
Signing in with a provider whose identity is linked to no account never joins an existing account just because the email matches. It creates a new account when sign-up is open, or fails with New accounts cannot be created this way when it is closed. A new account also needs an email the provider has verified; otherwise sign-up is refused (oauth_email_unverified). Link from the signed-in Security screen instead.
Passwords
The same rule is enforced on every path that sets a password (install, sign-up, reset): at least 8 characters, at most 256, at least one number, at least one of $ ! @ % & * # ^ ( ) _ + = -, and no leading or trailing space. Every one of those paths also refuses a password that appears in a known data breach (password_breached); see Password safety.
Change your password
Account menu → Security → Change password (signed in; an account that has a password).
Type your Current password, then the New password (at least 8 characters, a number and a symbol, no space at either end) and Confirm new password.
Change password. The screen says Password changed — Every other device was signed out. This one stays signed in.
The current password is checked first, and wrong guesses are limited like every other password check (5 a minute). A new password must differ from the current one and must not be a known breached password. An account that signs in only with a passkey or a linked provider has no password to change, and there is no screen to set one yet (no_password).
Permanent actions
An organization owner can turn on Manage Organization → Security → Ask people to confirm it is them before permanent actions. It is off by default; turning it on is recommended, and the change is recorded in the audit trail. When it is on, these actions each ask the person for proof first:
- delete a project, an environment, a server, a managed database, or a database inside one;
- remove a member from the organization (or leave it);
- revoke a license key, or a server's key.
The app opens Confirm it is you and asks for your password, or the 6-digit authentication code from your authenticator app if two-factor is on. Once confirmed, the action goes through, and every permanent action on that sign-in is unlocked for five minutes; signing in counts as a confirmation, so nobody is asked straight after signing in. An account with neither a password nor an authenticator (passkey or provider sign-in only) is told to sign in again, then repeat the action.
Over the API the first call answers 403 reauth_required with the action, the methods accepted (password, totp or signin) and windowSeconds. Prove it with POST /auth/reauth ({ "password": "…" } or { "code": "…" }), which answers expiresAt, then repeat the call. A wrong proof is Reauthentication failed (403) and too many attempts is Too many attempts (429). This five-minute confirmation is separate from the 15-minute rule that guards changing how you sign in, described above.
Reference
| Item | Value |
|---|---|
| Session lifetime | 7 days (cookie, HttpOnly, SameSite=Lax) |
| Re-authentication | password accounts: the current password, always; password-less accounts and provider linking: a session younger than 15 minutes. Password checks are limited to 5 a minute per account |
| Two-factor challenge | 5 minutes, single use |
| Two-factor lockout | five wrong codes within 15 minutes lock two-factor sign-in until the 15 minutes end; each code works once |
| Authenticator | RFC 6238 TOTP, six digits |
| Backup codes | 10, one-time, shown once; regenerate replaces all |
| Passkeys | WebAuthn, user verification required, discoverable, ES256 / RS256; web only |
| Email verification | 24-hour link; skipped when the control plane sends no mail |
| Password | 8–256 characters, a number, a special character, no edge whitespace |
| Permanent-action confirmation | organization setting, off by default; password or authenticator code; unlocks for 5 minutes on that sign-in |
| Sessions revoked on | 2FA on/off, backup codes regenerated, passkey added/removed, provider linked/unlinked (all but the current); password reset (all) |
Errors
Account routes answer with a short error string; the app shows the sentence in the table.
| Error | Status | Meaning |
|---|---|---|
Invalid credentials | 401 | Wrong email or password — the same answer whether or not the account exists. |
Verify your email before signing in… | 403 | The sign-up link has not been opened yet. |
Reauthentication required | 403 | Type your current password (a password-less account: sign in again — the 15-minute window has passed). |
reauth_required | 403 | The organization asks for confirmation before this permanent action. Prove it with POST /auth/reauth and repeat. |
reauth_unavailable | 400 | POST /auth/reauth on an account with no password and no authenticator; sign in again instead. |
incorrect_current_password | 400 | Change password: the current password is wrong. (400, not 403: the app treats a 403 as "ask again".) |
password_unchanged | 400 | Change password: the new password equals the current one. |
password_breached | 400 | The password appears in a known data breach. Choose another. Returned by sign-up, invitation sign-up, password reset and change password. |
no_password | 409 | Change password on an account that signs in without a password. |
Too many requests | 429 | Too many password checks for this account in a minute; wait and try again. |
two_factor_enabled | 409 | Two-factor is already on; disable it before enrolling again. |
Not enrolled, Invalid code | 400 | Verify was called without a pending enrollment, or the code did not match. A code that was already used also answers Invalid code. |
Invalid or expired challenge | 400 | The 5-minute sign-in challenge is over, or it was already used to sign in; sign in again. |
Too many attempts | 429 | Five wrong codes within 15 minutes; two-factor sign-in stays locked until the 15 minutes end. Signing in again does not reset it. |
passkey_exists | 409 | That authenticator is already registered on the account. |
Invalid credential | 400 | The browser's WebAuthn response did not verify (wrong origin, counter went backwards, no user verification). |
Not found | 404 | The passkey is not on this account. |
last_sign_in_method | 409 | Unlinking would leave no password, passkey or other provider. |
oauth_state_invalid | redirect | Sign-in expired or was started in another browser. Try again. — the 10-minute provider round trip took too long, was tampered with, or came back in a different browser from the one that started it. A sign-in in progress while the control plane was upgraded also fails this way once. |
oauth_email_unverified | redirect | The provider has not verified this email address, so it cannot be used to create an account. Verify it with the provider, or sign up with a password. |
oauth_exchange_failed | redirect | Could not complete sign-in. Try again. — the provider refused the code exchange. |
oauth_signup_disabled | redirect | New accounts cannot be created this way. — no linked account and sign-up is closed. |
account_conflict | redirect | The provider identity belongs to another user. |
account_disabled | redirect | The account is disabled. |
oauth_reauth_required, oauth_unauthenticated | redirect | Linking needs a recent sign-in, or the session ended during the round trip. |
Not configured | 503 | The control plane has no root secret or that provider is not set up. |
Related
- Accounts and access — the operator's side: configuring GitHub and Google on the control plane.
- Organizations, teams and access — invitations and grants.
- Password safety — the breached-password check.
- Security — sessions, secrets and the trust model.
Last updated on
Notifications
The bell, the events TurboPanel can tell you about, channels — email, webhook, Slack, Discord, Telegram — rules that decide what reaches each one, delivery and retries, digest and quiet hours, and every refusal code
Organizations, teams and access
How organizations, teams, workspaces and grants fit together, the four permissions and what each unlocks, inviting a teammate with or without an account, assigning and revoking grants, and every refusal