M4-T08: add two-step login and TOTP settings panel (qrcode.react)

This commit is contained in:
2026-06-21 22:42:41 +02:00
parent 51da8c5716
commit ee1264b66b
10 changed files with 1405 additions and 51 deletions
+270
View File
@@ -245,6 +245,7 @@ export interface paths {
*
* On success, sets an HttpOnly session cookie and returns the session user + CSRF token.
* On failure, returns 401 with no cookie set.
* Repeated failures trigger exponential back-off (429 + Retry-After).
* No X-CSRF-Token required (unauthenticated endpoint).
*/
post: operations["post_login_api_auth_login_post"];
@@ -300,6 +301,112 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/auth/totp/setup": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Post Totp Setup
* @description Generate a new pending TOTP secret, otpauth URI, and one-time recovery codes.
*
* The secret is stored in the DB but TOTP is NOT yet enabled (totp_enabled stays
* False until the user confirms with POST /api/auth/totp/enable).
*
* Recovery codes are returned here as plaintext exactly once; their Argon2 hashes
* are persisted immediately so enable only needs to flip the enabled flag.
*
* Repeating this call replaces any prior pending secret and regenerates codes.
*
* Requires: session cookie + X-CSRF-Token.
*/
post: operations["post_totp_setup_api_auth_totp_setup_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/auth/totp/enable": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Post Totp Enable
* @description Enable TOTP by confirming with the current 6-digit code from the authenticator app.
*
* Requires a prior call to POST /api/auth/totp/setup (so that a pending secret
* exists). On success, totp_enabled becomes True.
*
* Returns 400 if the code is wrong or there is no pending secret.
* Requires: session cookie + X-CSRF-Token.
*/
post: operations["post_totp_enable_api_auth_totp_enable_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/auth/totp/disable": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Post Totp Disable
* @description Disable TOTP. The caller must provide exactly one of:
* - ``password``: the user's current login password, OR
* - ``code``: the current 6-digit TOTP code.
*
* On success: totp_enabled=False, totp_secret cleared, all recovery codes deleted.
* Returns 400 if neither credential matches or neither is provided.
* Requires: session cookie + X-CSRF-Token.
*/
post: operations["post_totp_disable_api_auth_totp_disable_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/auth/totp": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get Totp Status
* @description Return the current TOTP status for the authenticated user.
*
* Response contains only ``{"enabled": bool}``.
* Secret and recovery codes are NEVER returned here.
* Requires: session cookie only (no CSRF — read-only).
*/
get: operations["get_totp_status_api_auth_totp_get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/homeassistant/publish": {
parameters: {
query?: never;
@@ -510,6 +617,8 @@ export interface components {
username: string;
/** Password */
password: string;
/** Totp Code */
totp_code?: string | null;
};
/** PasswordChangeRequest */
PasswordChangeRequest: {
@@ -647,6 +756,50 @@ export interface components {
/** Status */
status: string;
};
/**
* TotpDisableRequest
* @description Disable TOTP by proving identity.
*
* Exactly one of ``password`` or ``code`` must be provided.
*/
TotpDisableRequest: {
/** Password */
password?: string | null;
/** Code */
code?: string | null;
};
/**
* TotpEnableRequest
* @description The user confirms setup by providing the 6-digit TOTP code.
*/
TotpEnableRequest: {
/** Code */
code: string;
};
/**
* TotpSetupResponse
* @description Returned once after a setup call.
*
* ``secret`` and ``recovery_codes`` are **one-time plaintext values**.
* They are never returned again by any subsequent API call.
* The frontend must display and instruct the user to save them before confirming.
*/
TotpSetupResponse: {
/** Secret */
secret: string;
/** Otpauth Uri */
otpauth_uri: string;
/** Recovery Codes */
recovery_codes: string[];
};
/**
* TotpStatusResponse
* @description Minimal status response — never exposes secret or recovery codes.
*/
TotpStatusResponse: {
/** Enabled */
enabled: boolean;
};
/** ValidationError */
ValidationError: {
/** Location */
@@ -1143,6 +1296,123 @@ export interface operations {
};
};
};
post_totp_setup_api_auth_totp_setup_post: {
parameters: {
query?: never;
header?: {
"X-CSRF-Token"?: string | null;
};
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["TotpSetupResponse"];
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
post_totp_enable_api_auth_totp_enable_post: {
parameters: {
query?: never;
header?: {
"X-CSRF-Token"?: string | null;
};
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["TotpEnableRequest"];
};
};
responses: {
/** @description Successful Response */
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
post_totp_disable_api_auth_totp_disable_post: {
parameters: {
query?: never;
header?: {
"X-CSRF-Token"?: string | null;
};
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["TotpDisableRequest"];
};
};
responses: {
/** @description Successful Response */
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
get_totp_status_api_auth_totp_get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["TotpStatusResponse"];
};
};
};
};
publish_from_homeassistant_homeassistant_publish_post: {
parameters: {
query?: never;