---
title: "Local-account login submit (authorization flow)"
method: POST
path: "/login"
tags: ["OAuth"]
---

# Local-account login submit (authorization flow)

`POST /login`

Authenticate the local account and rejoin the authorization flow.

Success never mints a JWT — it flows straight into code issuance (platform
client) or the consent handle (registered third-party client), exactly
where the IdP callback rejoins, and burns the single-use ``ls``.
Credential failures re-render the form with one generic message: lockout
state, unknown email, and wrong password are indistinguishable (no
user-enumeration response oracle), while the shared
``AuthService.authenticate`` core still increments the failed-login count
and applies the lockout threshold. An account flagged
``must_change_password`` authenticates but is told to rotate via the UI
first — the OAuth plane must not hand a fully-scoped token to a
temporary-password principal the UI would have boxed into
change-password-only.

## Response `200`

Successful Response

- unknown

## Other responses

- `400` — Bad Request
- `404` — Local-account login is unavailable (`auth.local_login.enabled=false`, or an external IdP is configured — `auth.idp.enabled=true` — which always wins): the route answers the framework's plain route-not-found 404, so the gate state is unobservable.
- `422` — Unprocessable Entity
- `500` — Internal Server Error
- `503` — Service Unavailable

## Changes

- **2026-09-07** `b02360e6c1e1` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/jentic/apis/jentic-control-plane-api/changes/login/post.md)

---

[API](https://skmtc.dev/jentic/apis/jentic-control-plane-api.md) · [All operations](https://skmtc.dev/jentic/apis/jentic-control-plane-api/llms.txt) · [OpenAPI document](https://skmtc.dev/jentic/apis/jentic-control-plane-api/revisions/e4688b93dfc7?raw)
