mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: add auth extension points and one ssh connect pipeline
This commit is contained in:
1 parent
d3c209af13
commit
d774df8b41
159 files changed
+43243
-7451
No files matched your search
@@ -0,0 +1,169 @@
|
||||
import {
|
||||
authApi,
|
||||
handleApiError,
|
||||
markUserAuthenticated,
|
||||
type AuthResponse,
|
||||
} from "@/main-axios";
|
||||
|
||||
export interface PublicLoginMethod {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
kind: "redirect" | "form";
|
||||
labelKey: string;
|
||||
icon?: string;
|
||||
instances: Array<{ id: string; label: string }>;
|
||||
}
|
||||
|
||||
export interface SecondFactorRef {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
labelKey: string;
|
||||
}
|
||||
|
||||
export type LoginResponse = AuthResponse & {
|
||||
requires_second_factor?: boolean;
|
||||
second_factors?: SecondFactorRef[];
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
function remember(data: LoginResponse): LoginResponse {
|
||||
if (data?.token) localStorage.setItem("jwt", data.token);
|
||||
if (data?.success && !data.requires_totp) markUserAuthenticated();
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Enabled login methods for the login screen. Public. */
|
||||
export async function getLoginMethods(): Promise<PublicLoginMethod[]> {
|
||||
try {
|
||||
const response = await authApi.get("/users/auth/methods");
|
||||
return Array.isArray(response.data?.methods) ? response.data.methods : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function submitLoginMethod(
|
||||
methodId: string,
|
||||
body: Record<string, unknown>,
|
||||
instanceId?: string,
|
||||
): Promise<LoginResponse> {
|
||||
try {
|
||||
const response = await authApi.post(
|
||||
`/users/auth/${encodeURIComponent(methodId)}/verify`,
|
||||
{ ...body, instanceId },
|
||||
);
|
||||
return remember(response.data);
|
||||
} catch (error) {
|
||||
throw handleApiError(error, "login");
|
||||
}
|
||||
}
|
||||
|
||||
export async function startLoginRedirect(
|
||||
methodId: string,
|
||||
params: {
|
||||
instanceId?: string;
|
||||
rememberMe?: boolean;
|
||||
desktopCallbackPort?: number;
|
||||
},
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await authApi.get(
|
||||
`/users/auth/${encodeURIComponent(methodId)}/start`,
|
||||
{
|
||||
params: {
|
||||
instance: params.instanceId,
|
||||
rememberMe: params.rememberMe,
|
||||
desktopCallbackPort: params.desktopCallbackPort,
|
||||
},
|
||||
},
|
||||
);
|
||||
return response.data?.redirectUrl as string;
|
||||
} catch (error) {
|
||||
throw handleApiError(error, "start login");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers a second factor. With no temp token the server reads the pending
|
||||
* login cookie a redirect method left.
|
||||
*/
|
||||
export async function verifySecondFactor(
|
||||
factorId: string,
|
||||
body: Record<string, unknown>,
|
||||
): Promise<LoginResponse> {
|
||||
try {
|
||||
const response = await authApi.post(
|
||||
`/users/auth/second-factor/${encodeURIComponent(factorId)}/verify`,
|
||||
body,
|
||||
);
|
||||
return remember(response.data);
|
||||
} catch (error) {
|
||||
throw handleApiError(error, "verify second factor");
|
||||
}
|
||||
}
|
||||
|
||||
export async function challengeSecondFactor(
|
||||
factorId: string,
|
||||
tempToken?: string,
|
||||
): Promise<unknown> {
|
||||
const response = await authApi.post(
|
||||
`/users/auth/second-factor/${encodeURIComponent(factorId)}/challenge`,
|
||||
{ temp_token: tempToken },
|
||||
);
|
||||
return response.data?.challenge ?? null;
|
||||
}
|
||||
|
||||
export interface UserSecondFactorSummary {
|
||||
pluginId: string;
|
||||
factorId: string;
|
||||
labelKey: string | null;
|
||||
available: boolean;
|
||||
}
|
||||
|
||||
export async function getUserSecondFactors(
|
||||
userId: string,
|
||||
): Promise<UserSecondFactorSummary[]> {
|
||||
try {
|
||||
const response = await authApi.get(
|
||||
`/users/admin/${encodeURIComponent(userId)}/second-factors`,
|
||||
);
|
||||
return response.data?.factors ?? [];
|
||||
} catch (error) {
|
||||
handleApiError(error, "list second factors");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetUserSecondFactors(userId: string): Promise<void> {
|
||||
try {
|
||||
await authApi.delete(
|
||||
`/users/admin/${encodeURIComponent(userId)}/second-factors`,
|
||||
);
|
||||
} catch (error) {
|
||||
throw handleApiError(error, "reset second factors");
|
||||
}
|
||||
}
|
||||
|
||||
export interface SshAuthProviderSummary {
|
||||
type: string;
|
||||
labelKey: string;
|
||||
descriptionKey?: string;
|
||||
pluginId: string;
|
||||
fields: Array<Record<string, unknown>>;
|
||||
credentialType: boolean;
|
||||
needsUserInteraction: boolean;
|
||||
supportsBackground: boolean;
|
||||
available: boolean;
|
||||
missingPlugin?: { id: string; name: string };
|
||||
}
|
||||
|
||||
export async function getSshAuthProviders(): Promise<SshAuthProviderSummary[]> {
|
||||
try {
|
||||
const response = await authApi.get("/ssh-auth/providers");
|
||||
return Array.isArray(response.data?.providers)
|
||||
? response.data.providers
|
||||
: [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user