> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.moyoinnovations.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.moyoinnovations.com/_mcp/server.

# Authentication

> How login, two-factor authentication, and sessions work in Moyo Suite.

Moyo Suite authenticates the web app with **email + password**, optional **two-factor
authentication**, and short-lived tokens — not a long-lived API key.

> **Note**
>
> Programmatic, third-party API-key access is planned but intentionally not
> available yet — keys need to be stored hashed, scoped, rotated, and audited
> server-side first. The `POST /api/v1/auth/login` flow below is what the
> Moyo Suite frontend itself uses; there is no separate developer API-key
> program today.

Every endpoint on this page is prefixed with `/api/v1` — e.g. `POST
/api/v1/auth/login`, not `POST /auth/login`.

## Logging in

```http
POST /api/v1/auth/login
{
  "email": "you@company.com",
  "password": "••••••••"
}
```

* If the account has **no** 2FA enabled, this returns `access_token` and
  `refresh_token` (JWTs, `token_type: "bearer"`) directly.
* If the account **has** 2FA enabled, it instead returns `requires_2fa: true`
  plus a short-lived `temp_token` — see below.

Other ways in: a magic link (`POST /api/v1/auth/magic-link` → emailed
one-time link → `POST /api/v1/auth/magic-verify`), or social/OAuth login.

## Two-factor authentication

2FA is TOTP-based (Google Authenticator, Authy, etc.), not a stub:

#### Set up

`POST /api/v1/auth/2fa/setup` returns a base32 secret, an `otpauth://`
URI to render as a QR code, and one-time backup codes.

#### Confirm

`POST /api/v1/auth/2fa/confirm` with a 6-digit code from your
authenticator app turns 2FA on.

#### Verify at login

After a login returns `requires_2fa: true`, call `POST
    /api/v1/auth/2fa/verify` with the `temp_token` and a 6-digit code to
receive the real `access_token`/`refresh_token`. This endpoint is
rate-limited to 5 attempts/minute.

You can disable 2FA (`POST /api/v1/auth/2fa/disable`, requires password + a
valid code), check status (`GET /api/v1/auth/2fa/status`), or regenerate
backup codes (`POST /api/v1/auth/2fa/backup-codes`).

## How tokens are used

Once you're authenticated, requests carry credentials one of two ways:

* **`Authorization: Bearer <access_token>`** header, or
* **HttpOnly cookies** the API sets on the login response — `moyo_session`
  and `moyo_refresh`, scoped to your organization's whole domain for SSO
  across Moyo Suite subdomains.

> **Note**
>
> The Moyo Suite web app itself doesn't read those SSO cookies directly. Its
> frontend makes a server-to-server call to `/api/v1/auth/login`, then writes
> its own first-party `accessToken`/`refreshToken` cookies from the JSON
> response. If you're calling the API directly (not through the Moyo Suite
> web app), the `moyo_session`/`moyo_refresh` cookies above are the ones that
> matter.

`POST /api/v1/auth/refresh` (with a body token or the `moyo_refresh`
cookie) rotates both tokens before they expire.

> **Warning**
>
> `refresh_token`s are long-lived credentials — never log them or expose them
> to client-side JavaScript. The cookie-based flow keeps them HttpOnly for
> exactly this reason.

## Sessions

The Moyo Suite web app warns you about 5 minutes before your access token
expires, with a **Stay logged in** option that calls `/auth/refresh` in the
background and extends your session in place — you're never silently
logged out mid-form. This is purely a frontend convenience; the API's own
expiry/refresh behavior above is unchanged either way.

You can see and manage every device you're signed in on:

| Endpoint                                   | What it does                           |
| ------------------------------------------ | -------------------------------------- |
| `GET /api/v1/auth/sessions`                | List active sessions                   |
| `POST /api/v1/auth/sessions/revoke`        | Revoke one session                     |
| `POST /api/v1/auth/sessions/revoke-others` | Sign out every session except this one |
| `POST /api/v1/auth/logout-all`             | Sign out everywhere                    |

## Signing up

`POST /api/v1/auth/signup` creates a brand-new organization and its first
admin user in a single call (`email`, `password`, `tenant_name`,
`subdomain`, `initial_modules`) — this is what powers
[Creating Your Organization](/getting-started/creating-your-organization).
Joining an *existing* organization instead happens via a team invite; see
[Team & Permissions](/guides/team-permissions).