> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowxi.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Login

Flowxi login is designed for **security, determinism, and device-level control**.

This endpoint authenticates a user using **email + password** and conditionally enforces
**TOTP-based 2FA**, depending on the user configuration.

The behavior described here reflects the **exact production logic** implemented in the
login controller, request validators, and rate limiters.

***

## Endpoint

`POST /api/v1/auth/login`

This endpoint is **public** (no token required).

***

## Request payload

### Required fields

These fields are always required and validated strictly.

* `email` (string)\
  User email address. Normalized (lowercased, trimmed) server-side.

* `password` (string)\
  User password.

* `device_id` (string)\
  Stable, unique identifier for the device.\
  **Mandatory** to enforce one-session-per-device.

* `device_type` (string)\
  Device category (e.g. `ios`, `android`, `web`).

* `device_name` (string)\
  Human-readable name shown in device/session listings.

### Optional fields

* `country` (string)\
  Optional metadata, stored with the session token.

***

## Localization

Before any authentication logic runs, locale is resolved using:

1. `X-App-Locale`
2. `Accept-Language`
3. `user.locale` (if authenticated)
4. fallback: `fr`

The resolved locale is applied to:

* response `message`
* validation errors
* authentication errors

The `code` field is **never localized**.

***

## Login outcomes

There are **two possible outcomes**: direct token issuance or a 2FA challenge.

***

### Case 1: Login success (2FA not enabled)

If the user does **not** have 2FA enabled:

1. Credentials are verified
2. Any existing token for the same `device_id` is revoked
3. A new token is issued atomically

```json theme={null}
{
  "message": "Login successful.",
  "code": "LOGIN_SUCCESS",
  "data": {
    "mfa_required": false,
    "access_token": "plain_text_token",
    "token_type": "Bearer",
    "account_status": "active",
    "user_id": 123
  }
}
```

Characteristics:

* exactly **one token per `device_id`**
* other devices remain logged in
* token issuance is transactional

***

### Case 2: 2FA required

If the user **has 2FA enabled**, no token is issued at this stage.

Instead, the API returns a **login challenge**.

```json theme={null}
{
  "message": "Two-factor verification required.",
  "code": "MFA_REQUIRED",
  "data": {
    "mfa_required": true,
    "challenge_id": "uuid",
    "otp_type": "totp",
    "expires_in": 300
  }
}
```

Characteristics of the challenge:

* stored in cache
* bound to:
  * IP address
  * User-Agent
  * `device_id`
* hard expiration (TTL is never extended)
* max 5 verification attempts

***

## Verify login with 2FA (second step)

### Endpoint

`POST /api/v1/auth/2fa/verify-login`

### Required fields

* `challenge_id` (UUID)\
  Identifier returned by the login step.

* `code` (string)\
  TOTP code from the authenticator app.

***

### Verification rules

* strict IP match
* strict User-Agent match
* maximum 5 attempts per challenge
* immediate invalidation on expiration
* no TTL extension on retries

Any mismatch invalidates the challenge.

***

### Successful verification response

```json theme={null}
{
  "message": "Login successful.",
  "code": "LOGIN_SUCCESS",
  "data": {
    "access_token": "plain_text_token",
    "token_type": "Bearer",
    "account_status": "active",
    "user_id": 123
  }
}
```

***

## Anti-enumeration guarantees

The login endpoint **never reveals** whether:

* the email exists
* the password is incorrect
* the account is inactive or blocked

All such failures return:

* HTTP `401`
* `code: INVALID_CREDENTIALS`

Frontend logic **must not** attempt to infer the cause.

***

## Rate limiting

Login attempts are rate-limited per `email + ip`.

* attempts are counted even if the user does not exist
* limits apply before password verification
* repeated failures result in `RATE_LIMITED` (429)

***

## Token issuance policy

When a token is issued:

* **1 active token per `device_id`**
* previous tokens for the same device are deleted **before** creation
* advisory locks (PostgreSQL) prevent concurrent duplication

This guarantees deterministic session behavior.

***

## Common frontend mistakes to avoid

* Treating `device_id` as optional
* Assuming login always returns a token
* Storing `challenge_id` long-term
* Retrying 2FA verification after expiration
* Using `message` instead of `code` for logic

***

## Summary

The login endpoint provides:

* deterministic authentication behavior
* strong anti-enumeration guarantees
* device-level session isolation
* first-class 2FA enforcement
* fully localized responses
