---
title: "Log in with email + password"
method: POST
path: "/auth/login"
tags: ["Auth"]
---

# Log in with email + password

`POST /auth/login`

## Request

- Base URL: `http://localhost:38200`
- URL: `http://localhost:38200/auth/login`
- Auth: none required

Sets the JWT access-token cookie and a rotating refresh-token cookie on success. Rate-limited to 10 requests/5min per IP, plus a per-(IP, email) brute-force gate (5 failures / 15min).

T731: when the account has CONFIRMED TOTP MFA, a correct password does NOT set session cookies. The 200 response instead carries AuthMfaChallengeResponse (`mfaRequired: true` + a short-lived `challenge`), and the caller must complete the session via POST /auth/login/mfa.

## Request body

- object
  - `email` string, email, required
  - `password` string, required
  - `turnstileToken` string

## Response `200`

Either a completed session (JWT + refresh cookies set), or -- when the account has confirmed MFA -- an MFA challenge with no cookies set. Distinguish by the presence of `mfaRequired`.

- union
  - AuthSessionResponse
    - `user` object
      - `id` integer
      - `email` string, email
      - `role` 'owner' | 'admin' | 'member'
      - `isSuperAdmin` boolean
      - `emailVerified` boolean
      - `createdAt` string, date-time
    - `tenant` object
      - `id` integer
      - `name` string
      - `slug` string
      - `plan` string
      - `createdAt` string, date-time
  - AuthMfaChallengeResponse: Returned by POST /auth/login instead of AuthSessionResponse when the user has confirmed MFA. No session cookies are set on this response -- the challenge must be redeemed via POST /auth/login/mfa within expiresIn seconds.
    - `mfaRequired` true
    - `challenge` string: Opaque, short-lived token. Pass back unchanged to POST /auth/login/mfa.
    - `expiresIn` integer: Challenge TTL in seconds (300).

## Other responses

- `400`: Request body/query failed Zod validation.
- `401`: Invalid email or password (also returned for deleted/SSO-only accounts — intentionally generic, non-disclosing).
- `429`: Rate limit exceeded for this endpoint.

---

[API](https://skmtc.dev/contextq/apis/contextq-api.md) · [All operations](https://skmtc.dev/contextq/apis/contextq-api/llms.txt) · [OpenAPI document](https://skmtc.dev/contextq/apis/contextq-api/revisions/7afc44056a22?raw)
