feat: move totp and passkeys into plugins (C1)

This commit is contained in:
LukeGus committed 2026-09-24 16:57:24 -05:00
1 parent f4f26a9b8b
commit 5c18435105
235 files changed
+22545 -5610

No files matched your search

+1 -88
View File
@@ -1,96 +1,9 @@
import { AxiosError } from "axios";
import type { TermixAlert } from "@/types";
import {
authApi,
handleApiError,
markUserAuthenticated,
type AuthResponse,
} from "@/main-axios";
import { authApi, handleApiError } from "@/main-axios";
// ALERTS
// ============================================================================
export async function setupTOTP(credential?: string): Promise<{
secret: string;
qr_code: string;
additional?: boolean;
}> {
try {
const response = await authApi.post("/users/totp/setup", { credential });
return response.data;
} catch (error) {
handleApiError(error as AxiosError, "setup TOTP");
throw error;
}
}
export async function enableTOTP(
totp_code: string,
): Promise<{ message: string; backup_codes: string[] }> {
try {
const response = await authApi.post("/users/totp/enable", { totp_code });
return response.data;
} catch (error) {
handleApiError(error as AxiosError, "enable TOTP");
throw error;
}
}
export async function disableTOTP(
password?: string,
totp_code?: string,
): Promise<{ message: string }> {
try {
const response = await authApi.post("/users/totp/disable", {
password,
totp_code,
});
return response.data;
} catch (error) {
handleApiError(error as AxiosError, "disable TOTP");
throw error;
}
}
export async function verifyTOTPLogin(
temp_token: string,
totp_code: string,
rememberMe: boolean = false,
): Promise<AuthResponse> {
try {
const response = await authApi.post("/users/totp/verify-login", {
temp_token,
totp_code,
rememberMe,
});
if (response.data.success) {
markUserAuthenticated();
}
return response.data;
} catch (error) {
handleApiError(error as AxiosError, "verify TOTP login");
throw error;
}
}
export async function generateBackupCodes(
password?: string,
totp_code?: string,
): Promise<{ backup_codes: string[] }> {
try {
const response = await authApi.post("/users/totp/backup-codes", {
password,
totp_code,
});
return response.data;
} catch (error) {
handleApiError(error as AxiosError, "generate backup codes");
throw error;
}
}
export async function getUserAlerts(): Promise<{
alerts: TermixAlert[];
}> {
-13
View File
@@ -194,19 +194,6 @@ export async function adminResetUserPassword(
return response.data;
}
export async function adminDisableUserTotp(
userId: string,
): Promise<{ message: string }> {
try {
const response = await authApi.post("/users/admin/totp/disable", {
userId,
});
return response.data;
} catch (error) {
handleApiError(error, "disable user TOTP");
}
}
export async function adminExportUserData(
userId: string,
): Promise<Record<string, unknown>> {
-127
View File
@@ -1,127 +0,0 @@
import type {
AuthenticationResponseJSON,
PublicKeyCredentialCreationOptionsJSON,
PublicKeyCredentialRequestOptionsJSON,
RegistrationResponseJSON,
} from "@simplewebauthn/browser";
import {
browserSupportsWebAuthn,
startAuthentication,
startRegistration,
} from "@simplewebauthn/browser";
import { authApi, handleApiError } from "@/main-axios";
export type WebAuthnUserVerification = "discouraged" | "preferred" | "required";
export type WebAuthnCredentialSummary = {
id: string;
name: string;
deviceType?: string | null;
backedUp: boolean;
transports: string[];
userVerification: WebAuthnUserVerification;
createdAt: string;
lastUsedAt?: string | null;
};
type RegistrationOptionsResponse = {
options: PublicKeyCredentialCreationOptionsJSON;
challengeId: string;
};
type AuthenticationOptionsResponse = {
options: PublicKeyCredentialRequestOptionsJSON;
challengeId: string;
};
export type PasskeyLoginResult = {
success: boolean;
requires_totp?: boolean;
temp_token?: string;
is_admin?: boolean;
username?: string;
userId?: string;
is_oidc?: boolean;
totp_enabled?: boolean;
token?: string;
};
export function isPasskeySupported(): boolean {
return browserSupportsWebAuthn();
}
export async function loginWithPasskey(
username?: string,
rememberMe = false,
): Promise<PasskeyLoginResult> {
try {
const optionsResponse = await authApi.post<AuthenticationOptionsResponse>(
"/users/webauthn/authenticate/options",
username ? { username } : {},
);
const credential = await startAuthentication({
optionsJSON: optionsResponse.data.options,
});
const verifyResponse = await authApi.post<PasskeyLoginResult>(
"/users/webauthn/authenticate/verify",
{
challengeId: optionsResponse.data.challengeId,
response: credential as AuthenticationResponseJSON,
rememberMe,
},
);
return verifyResponse.data;
} catch (error) {
throw handleApiError(error, "sign in with passkey");
}
}
export async function listWebAuthnCredentials(): Promise<{
credentials: WebAuthnCredentialSummary[];
}> {
try {
const response = await authApi.get("/users/webauthn/credentials");
return response.data;
} catch (error) {
throw handleApiError(error, "list passkeys");
}
}
export async function registerWebAuthnCredential(
name: string,
userVerification: WebAuthnUserVerification,
): Promise<{ success: boolean }> {
try {
const optionsResponse = await authApi.post<RegistrationOptionsResponse>(
"/users/webauthn/register/options",
{ userVerification },
);
const credential = await startRegistration({
optionsJSON: optionsResponse.data.options,
});
const verifyResponse = await authApi.post(
"/users/webauthn/register/verify",
{
challengeId: optionsResponse.data.challengeId,
name,
response: credential as RegistrationResponseJSON,
},
);
return verifyResponse.data;
} catch (error) {
throw handleApiError(error, "register passkey");
}
}
export async function deleteWebAuthnCredential(
credentialId: string,
): Promise<{ success: boolean }> {
try {
const response = await authApi.delete(
`/users/webauthn/credentials/${credentialId}`,
);
return response.data;
} catch (error) {
throw handleApiError(error, "delete passkey");
}
}