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

+3
View File
@@ -350,6 +350,9 @@ export function createPluginApp(
titleKey: key(method.titleKey),
pluginId,
component: scoped(method.component) as never,
enrollment: method.enrollment
? (scoped(method.enrollment) as never)
: undefined,
}),
);
},
+8 -3
View File
@@ -11,9 +11,9 @@ import { createRegistry } from "@/lib/registry";
* SSH auth editors render in the host and credential editors when their auth
* type is picked. Login methods render on the login screen for methods the
* server reports as enabled; second factors render in the step after a first
* login and, when they bring one, in Settings > Security for enrolment.
* Core's own (OIDC, LDAP, passkeys, TOTP) register from
* src/ui/auth/legacy-auth-ui.tsx until they move into plugins.
* login. Either kind may bring an enrolment section for Settings > Security.
* Core's own (OIDC, LDAP) register from src/ui/auth/legacy-auth-ui.tsx until
* they move into plugins.
*/
export interface SshAuthEditorDef {
/** The authType value stored on the host. */
@@ -34,6 +34,9 @@ export interface LoginMethodDef {
titleKey: string;
icon?: ComponentType<{ className?: string }>;
component: ComponentType<LoginMethodUIProps>;
/** Also drawn under the password form. */
placement?: "inline";
enrollment?: ComponentType<Record<string, unknown>>;
}
export interface SecondFactorDef {
@@ -55,10 +58,12 @@ export const useSshAuthEditors = sshAuthEditors.useList;
export const registerLoginMethod = loginMethods.register;
export const getLoginMethodUI = loginMethods.get;
export const useLoginMethods = loginMethods.useList;
export const listLoginMethodUIs = loginMethods.list;
export const registerSecondFactor = secondFactors.register;
export const getSecondFactorUI = secondFactors.get;
export const useSecondFactors = secondFactors.useList;
export const listSecondFactorUIs = secondFactors.list;
export function resetAuthRegistries(): void {
sshAuthEditors.reset();
+49
View File
@@ -42,6 +42,12 @@ import {
listSettingsComponents,
} from "@/settings/settings-components";
import { getSlotContributions, listActions } from "@/shell/action-registry";
import {
getLoginMethodUI,
getSecondFactorUI,
listLoginMethodUIs,
listSecondFactorUIs,
} from "./auth-registry";
import { ActionSlot, ComponentSlot } from "@/shell/ActionSlot";
import type { Host, Tab } from "@/types/ui-types";
import { createPluginApp } from "./app";
@@ -223,6 +229,9 @@ export async function renderPlugin(
(contribution) => contribution.actionId,
),
actions: () => mine(listActions()).map((action) => action.id),
loginMethods: () => mine(listLoginMethodUIs()).map((method) => method.id),
secondFactors: () =>
mine(listSecondFactorUIs()).map((factor) => factor.id),
},
renderTab(type, props = {}) {
@@ -315,6 +324,46 @@ export async function renderPlugin(
);
},
renderLoginMethod(id, props = {}) {
const method = getLoginMethodUI(id) ?? missing("a login method", id);
const Component = method.component;
return wrap(
<Component
methodId={id}
instances={[]}
rememberMe={false}
disabled={false}
submit={async () => {}}
startRedirect={async () => {}}
complete={async () => {}}
{...props}
/>,
);
},
renderSecondFactor(id, props = {}) {
const factor = getSecondFactorUI(id) ?? missing("a second factor", id);
const Component = factor.component;
return wrap(
<Component
factorId={id}
rememberMe={false}
disabled={false}
verify={async () => {}}
challenge={async () => null}
cancel={() => {}}
{...props}
/>,
);
},
renderEnrollment(id) {
const entry = getSecondFactorUI(id) ?? getLoginMethodUI(id);
const Component =
entry?.enrollment ?? missing("an enrolment section", id);
return wrap(<Component />);
},
renderSlot(slotId, props = {}) {
return wrap(
<>