# Sign-in codes

Source: https://docs.settlemint.com/docs/getting-started/sign-in-codes
Email and text message codes are a second factor at Investor portal sign-in, offered when the account and its organisations allow them.

An investor who has turned on two-factor authentication can have more than one way to finish signing in. Beside an authenticator app, the platform accepts a six-digit code sent to the account's email address, or by text message to a verified phone number already stored on the account, when the investor's organisations allow it. Both are second factors: the password is still the first. They are off by default, and an organisation owner turns them on through the API, so this page serves the owner who sets the methods and the support team that helps investors sign in.

The codes belong to Investor portal sign-in. Console sign-in takes an authenticator code or a backup code only. The Console's [Security](https://docs.settlemint.com/docs/console/my-account/security) page and the Investor portal's own Security screen set up an authenticator app and passkeys, and neither offers a control for email and text message codes.

## Before you start [#before-you-start]

- The account has two-factor authentication enabled. An investor turns it on with an authenticator app, or with the codes themselves through the authentication API: `POST /api/auth/two-factor/enable` from their signed-in session, with their `password` and `method: "otp"`.
- Every organisation the investor belongs to allows the method. An organisation owner sets that through the API, as [Which methods an organisation allows](#which-methods-an-organisation-allows) shows, and a member sees a method only when all their organisations allow it. Both code methods are off by default.
- For a text message, the account has a verified phone number. No screen and no API route records or verifies a phone number, so an account offers the text message method only when a verified number is already stored on it.
- For a text message, the deployment's SMS provider is configured. Without a provider configured, the send answers `TWO_FACTOR_DELIVERY_FAILED` and no code arrives.

## Steps [#steps]

**Step 1 of 4**

### Sign in with the password [#sign-in-with-the-password]

Open the Investor portal sign-in screen and enter the email address and password. The platform accepts the password and, because two-factor is on, answers a challenge instead of a session. The Portal opens "Two-factor authentication" over the sign-in page.

When the account allows more than one method, the dialog shows a button for each: "Authenticator app", "Email code" or "Text message", with the first one selected. With one method available it shows that method with no choice to make. When no method is available, the sign-in screen reads `No enabled two-factor method is available for this account. Ask an organization administrator to enable one.` and opens no dialog.

**Step 2 of 4**

### Choose where the code goes [#choose-where-the-code-goes]

Select "Email code" to receive the code at the account's email address, or "Text message" to receive it at the verified phone number. The dialog reads `Choose where to receive your 6-digit sign-in code.`

Select **Send code**. "Verify" cannot be selected until a code has been sent.

**Step 3 of 4**

### Enter the code [#enter-the-code]

Type the six digits into `6-digit sign-in code` and select **Verify**. A correct code opens the portal. A wrong one shows "Invalid code" and counts towards the attempt budget. A code older than five minutes shows "OTP has expired".

The email carries the same code in its body and in an `OTP-Token` header bound to the address of the sign-in screen, so a mail client can offer to fill it in on that site. The header is written only for an https address or a loopback address. Any other address sends the mail without it.

**Step 4 of 4**

### Send another code [#send-another-code]

Select "Resend code" after the wait. The button reads "Resend code in" the remaining seconds while the wait runs, and the platform refuses an earlier request with `429 Too Many Requests` and `TWO_FACTOR_RESEND_THROTTLED`.

A new code replaces the previous one. The older code stops working the moment the new one is sent.

## What each code obeys [#what-each-code-obeys]

| Rule | Value |
| --- | --- |
| Length | Six digits |
| Lifetime | Five minutes from the send |
| Uses | One. A code that was accepted cannot be accepted again. |
| A second send | Replaces the earlier code and ends its life |
| Wait between sends | 30 seconds |
| Wrong codes before a lock | Five within 15 minutes. The sixth attempt is refused. |
| Lock length | 15 minutes |

Wrong email codes, text message codes, authenticator codes and backup codes share the same budget. The sixth wrong code in the window is refused before its code is checked, with `429 Too Many Requests` and code `LOGIN_TWO_FACTOR_LOCKED`, and the answer states how many seconds remain. A correct code does not clear a lock that is already in force. The lock ends when its time passes.

## Which methods an organisation allows [#which-methods-an-organisation-allows]

A method is available only when it is allowed by every organisation the investor belongs to. The platform intersects the policies: one organisation that leaves email codes out removes them for a member who belongs to it, however the other organisations are set. An archived organisation does not count.

New organisations start with the authenticator app alone. No Console screen changes the set. An organisation owner changes it with `POST /api/auth/organization/update` from a signed-in session, since an API key is refused on this route. The body sends `allowedTwoFactorMethods` as a JSON array of `totp`, `email-otp` and `sms-otp`, written as a string, and applies to the active organisation unless it names another with `organizationId`:

```json
{ "data": { "allowedTwoFactorMethods": "[\"totp\",\"email-otp\"]" } }
```

An empty array or an unknown method is refused with `400 Bad Request` and `INVALID_TWO_FACTOR_METHODS`. A saved change is an entry on the organisation's [Audit trail](https://docs.settlemint.com/docs/console/reporting/audit-trail) with the methods before and after.

Note (Keep totp in the list while members sign in to the Console): Console sign-in accepts only an authenticator code or a backup code, and both are refused with `TWO_FACTOR_METHOD_DISABLED` when an organisation the member belongs to leaves `totp` out. A member with two-factor on then cannot finish Console sign-in.

The platform decides which buttons the dialog shows. It returns a method only when the organisation policies allow it and the account can receive it, so a text message disappears from the dialog as soon as the phone number stops being verified.

## What each failure means [#what-each-failure-means]

| Answer | Code | What it means | What to do |
| --- | --- | --- | --- |
| `403 Forbidden` | `TWO_FACTOR_METHOD_DISABLED` | The organisation policy does not allow that method, or the account cannot receive it. | Ask an organisation owner to allow it, or choose another method. |
| `403 Forbidden` | `TWO_FACTOR_METHOD_UNAVAILABLE` | No allowed method is one the account can receive. The sign-in screen says so. | An organisation owner allows a method the account can receive. |
| `400 Bad Request` | `TWO_FACTOR_METHOD_REQUIRED` | The request named no method. | Choose a method before sending a code. |
| `429 Too Many Requests` | `TWO_FACTOR_RESEND_THROTTLED` | A code was requested less than 30 seconds ago. | Wait the seconds the answer names. |
| `429 Too Many Requests` | `LOGIN_TWO_FACTOR_LOCKED` | Five wrong codes were entered inside the window. | Wait for the lock to pass, then request a new code. |
| `503 Service Unavailable` | `TWO_FACTOR_DELIVERY_FAILED` | The email or the SMS provider did not accept the message. | Send again at once. |

When delivery fails, the platform discards the code it could not deliver and lifts the 30 second wait, so the investor can send again at once and loses no attempt.

## Troubleshooting [#troubleshooting]

**The dialog offers only the authenticator app**

At least one organisation the investor belongs to leaves both codes out, so check each membership, not only the
organisation the investor is working in. When the organisations allow text messages but not email codes, the account
also needs a verified phone number. An organisation owner changes the policy.

**Verify says Invalid code or OTP has expired**

"Invalid code" means a wrong code, including an older code that a newer send replaced, and it counts towards the
attempt budget. "OTP has expired" means the code passed its five minutes. Request a new code and enter the latest one.
If five codes were already wrong, the lock answers with the seconds left.

**No email arrives**

Check the address on the account, then send another code after the 30 second wait. A send that failed on the provider
side answers `TWO_FACTOR_DELIVERY_FAILED`, costs no attempt and can be repeated at once.

**No text message arrives**

SMS needs a verified phone number and a configured SMS provider on the deployment. A number that is present but
unverified is treated as absent, and the method is not offered. Confirm with the deployment operator how the number is
stored before you promise an investor this method.

**The investor wants codes instead of an authenticator app**

Turn two-factor on with `POST /api/auth/two-factor/enable`, the account password and `method: "otp"`, then have every
organisation the investor belongs to allow `email-otp`, `sms-otp`, or both. The Console's Security page sets up the
authenticator app and passkeys and does not manage these codes.

## Frequently asked questions [#frequently-asked-questions]

### Is this the same two-factor authentication as the Console's Security page?

Yes for the authenticator app. Both Security screens set up the same authenticator app and passkeys on the same account. Email and text message codes finish sign-in at the Investor portal only, and the authentication API turns them on for an account.

### Does a code replace the password?

No. The password is checked first, and the code answers the challenge that follows. A wrong password never reaches the code step.

### Can an investor turn a policy-enabled method off for their own account?

No. The platform offers every method that all the investor's organisations allow and that the account can receive. An investor who does not want codes asks an organisation owner to change the policy.

### Do sign-in codes work when the deployment uses single sign-on?

Yes. With an identity provider configured, the Console signs in only through the provider, but the Investor portal keeps password sign-in for investors, and the codes finish that sign-in as described here. [Single sign-on](https://docs.settlemint.com/docs/deployment/sso#investor-portal-sign-in-under-single-sign-on) covers what changes.

## You should see [#you-should-see]

- The Portal opens on the page you asked for.
- No code from an earlier send is accepted afterwards.
- The account's session list shows the new session's device, organisation and expiry, like any other session.
