# Two-factor authentication

> Protect console sign-ins with an authenticator app and recovery codes, and require two-factor authentication for every member of your organization.

Two-factor authentication (2FA) adds a second step to signing in to the MergeHarbor console: after your password, or after GitHub, GitLab, Azure DevOps, Bitbucket or Google, you enter a 6-digit code from an authenticator app. It is available on every plan and in self-hosted deployments.

## Turn it on for your account

1. Open **Your account (account menu) → Two-factor authentication** and choose **Set up two-factor authentication**.
2. Scan the QR code with an authenticator app (1Password, Google Authenticator, Microsoft Authenticator, Authy, or any app that supports time-based one-time passwords). If you can't scan it, type the setup key instead. The QR code is drawn in your browser; the key is never sent to a third party.
3. Enter the 6-digit code the app shows to confirm. Nothing changes until you confirm, and an unconfirmed set-up expires after 30 minutes.
4. Save your **10 recovery codes**. Each one signs you in once if you lose your device. They are shown only at this moment, so copy or download them into your password manager.

From then on, every console sign-in asks for a code after the first step. The code changes every 30 seconds; codes from 30 seconds either side are accepted to allow for clock drift, and each code works only once.

## Signing in

After your password (or sign-in provider) is accepted, MergeHarbor shows the two-factor step. No session exists until you finish it. Enter the current code, or choose **Use a recovery code instead**.

- The step must be completed within **5 minutes** and allows **5 attempts**; after that, log in again.
- Repeated wrong codes lock two-factor sign-in for the account for a while (1 minute, doubling up to an hour), across new sign-in attempts too.
- Signing in with a recovery code uses it up. When you're running low, the console reminds you to generate a new set.

## Recovery codes and turning it off

Under **Your account (account menu) → Two-factor authentication**:

- **Regenerate recovery codes** issues a new set of 10 after you confirm with a current code. The previous codes stop working immediately.
- **Turn off** needs your password (accounts that have no password need to have signed in within the last 10 minutes) and a current code or a recovery code. You can't turn two-factor authentication off while you belong to an organization that requires it.

If you lose both your device and your recovery codes, contact your organization's MergeHarbor administrator or [MergeHarbor support](https://mergeharbor.com/contact?topic=support). On a self-hosted instance, the operator can reset it by deleting your row from the `user_mfa` table.

## Require it for your organization

An owner can require two-factor authentication for every member under **Settings → Security → Policies → Two-factor authentication**. The page shows how many members already use it, and the **People** page shows a **2FA** or **No 2FA** badge for each member.

- To avoid locking yourself out, turn on two-factor authentication for your own account first.
- Members without it are taken through set-up the next time they open the organization, and can't reach it until they finish. Members whose current session started before they turned it on are asked for a code.
- **API keys and SCIM tokens are not affected.** They are separate credentials; rotate and scope them as before.
- Your other organizations are not affected: the requirement applies to this organization only.

### With single sign-on

When members sign in through your organization's [single sign-on](https://mergeharbor.com/docs/sso) (OpenID Connect or SAML), multi-factor authentication is **your identity provider's responsibility**: configure MFA in Okta, Microsoft Entra ID, Google Workspace or your IdP, and MergeHarbor does not ask for a second code. An SSO session for your organization satisfies the requirement.

When single sign-on is enforced, the requirement therefore only matters for break-glass owners who sign in without SSO; we recommend turning it on so those accounts are protected too.

## Audit log

Two-factor events appear in **Settings → Audit log** of every organization the person belongs to:

| Event                           | When                                                                                                              |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `mfa.enable`                    | A member turned on two-factor authentication.                                                                     |
| `mfa.disable`                   | A member turned it off.                                                                                           |
| `mfa.recovery_codes.regenerate` | A member generated new recovery codes.                                                                            |
| `mfa.challenge.failed`          | Wrong codes were entered for a member's account (recorded at most once a minute per account, plus every lockout). |
| `org.mfa_required.update`       | An owner turned the organization's requirement on or off.                                                         |
| `auth.login`                    | Each sign-in; `changes.mfa` shows which second factor was used.                                                   |

## Security details

- Codes follow RFC 6238 (TOTP: HMAC-SHA1, 6 digits, 30-second steps), the default of every authenticator app.
- The secret is encrypted at rest with the server's `SECRETBOX_KEYS` (AES-GCM, bound to your account). Self-hosted instances need `SECRETBOX_KEYS` set to offer two-factor authentication.
- Recovery codes are stored only as keyed hashes.
- Codes are compared in constant time, and the last accepted time step is remembered so a code cannot be replayed.
- The pending sign-in lives in a short-lived, HttpOnly cookie scoped to the two-factor endpoints; the session cookie is set only after the second factor.

The endpoints are documented in your console's API reference (`/api/docs/`) under **accounts** (`/auth/mfa/*`, `/api/v1/me/mfa*`) and **security** (`/api/v1/orgs/{org}/mfa-policy`).
