mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
feat: move totp and passkeys into plugins (C1)
This commit is contained in:
1 parent
f4f26a9b8b
commit
5c18435105
235 files changed
+22545
-5610
No files matched your search
@@ -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[];
|
||||
}> {
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user