Skip to main content
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
{
  "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.
{
  "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

{
  "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