Skip to content

apex-surface-auth

The standard pattern for public apex auth: login, signup, forgot-password, and account-deletion on your marketing surface — schema-less, anonymously reachable, without leaking admin nav or internal topology.

A typical app has two mounts sharing locale and primitives:

  • Admin UI → createKumikoApp (loads the full schema after login via the authenticated GET /api/schema)
  • Apex → createPublicSurface (no schema, ever)
  • Four public routes — /login, /signup, /forgot-password, /delete-account (+ confirm) using bundled auth screens.
  • createPublicSurface — stacks clientFeatures providers and i18n only, not their gates (an AuthGate would lock the public surface).
  • AuthShellProvider — auth card inside marketing chrome instead of fullscreen (optional; default fullscreen still works).
  • composeApexAccountApp() — server feature list for all four flows.
  • Anonymous deletion — email-verified magic-link flow when the user cannot log in (GDPR Art. 17 lockout case).
  • anonymousAccess — lets unauthenticated /api/write hit anonymous handlers on the apex host tenant.
import {
ForgotPasswordScreen,
SignupScreen,
createLoginRoute,
AuthShellProvider,
emailPasswordClient,
} from "@cosmicdrift/kumiko-bundled-features/auth-email-password/web";
import {
RequestAccountDeletionScreen,
ConfirmAccountDeletionScreen,
} from "@cosmicdrift/kumiko-bundled-features/user-data-rights/web";
import { createPublicSurface } from "@cosmicdrift/kumiko-renderer/web";
// createLoginRoute, not a raw LoginScreen render — it owns the challenge-
// swap for a second factor, so mounting auth-mfa later doesn't need this
// route touched again. Pass `mfaVerifyScreen: MfaVerifyScreen` (from
// `.../auth-mfa/web`) once the app mounts auth-mfa.
const LoginRoute = createLoginRoute({ loginScreenProps: { signupHref: "/signup" } });
createPublicSurface({
clientFeatures: [emailPasswordClient()], // SessionProvider + i18n
shell: ({ children }) => (
<MarketingChrome>
<AuthShellProvider shell={(card) => <div className="py-12 flex justify-center">{card}</div>}>
{children}
</AuthShellProvider>
</MarketingChrome>
),
routes: [
{ path: "/login", component: <LoginRoute /> },
{ path: "/signup", component: <SignupScreen loginHref="/login" /> },
{ path: "/forgot-password", component: <ForgotPasswordScreen loginHref="/login" /> },
{ path: "/delete-account", component: <RequestAccountDeletionScreen /> },
{ path: "/delete-account/confirm", component: <ConfirmAccountDeletionScreen /> },
],
fallback: <LoginRoute />,
});

composeApexAccountApp() (see src/feature.ts) mounts features for all four flows. Login/signup/password reset come from auth-email-password; the anonymous email-verified deletion flow from user-data-rights:

  • request-deletion-by-email (anonymous, enumeration-safe) → magic link
  • confirm-deletion-by-token (anonymous) → starts grace period
runProdApp({
envSchema, // composeEnvSchema({ core: frameworkCoreEnvSchema, features })
features: composeApexAccountApp({
deletionTokenSecret: process.env.DELETION_TOKEN_SECRET!,
deletionVerifyUrl: "https://app.example.com/delete-account/confirm",
sendDeletionVerificationEmail: async ({ email, verifyUrl }) => {
// Must be non-blocking (enqueue) — synchronous send enables timing oracle.
await delivery.notify("account.deletion.verify", { email, verifyUrl });
},
}),
anonymousAccess: { defaultTenantId: APEX_TENANT_ID },
});

GDPR Art. 17 applies when the user cannot log in (lockout). The flow is email-verified (magic link), not login-gated. The HMAC token carries userId

  • expiry (no DB table); purpose "deletion-request" blocks replay on other token endpoints. Second confirm is idempotent (user_not_in_active_state).

Needs a running Postgres and TEST_DATABASE_URL set (e.g. postgres://postgres:postgres@127.0.0.1:5432/postgres, see demo/.env.example).

Terminal window
bun test src/__tests__/feature.integration.test.ts

Proves end-to-end over real /api/write without auth:

  1. request-deletion-by-email → verification email enqueued
  2. confirm-deletion-by-token → user status DeletionRequested
  3. Unknown email → same 200 response, no mail (enumeration-safe)
  • user-profile — logged-in self-service profile (password, email, deletion while authenticated).
  • session-revocation — wire createSessionCallbacks() so logout invalidates JWTs server-side.

The feature entry point — embedded straight from the source file, so the code here is exactly what runs. Multi-file samples keep their remaining files next to it on GitHub (link below):

// apex-surface-auth recipe — the evident way for public apex content.
//
// A Kumiko app has a public apex presence (landing/login) AND a
// schema-driven admin UI. The admin UI mounts via createKumikoApp (full
// schema). The apex mounts via createPublicSurface — schema-LESS, anonymously
// reachable, no admin nav/topology leak. Both share locale + primitives.
//
// This recipe shows:
// 1. Server: the feature composition for the 4 account flows + the anonymous,
// email-verified deletion flow (lockout-safe).
// 2. Client (comment below + README): how createPublicSurface + AuthShell
// mount the screens in the apex chrome.
//
// CLIENT-WIRING (the app's apex.tsx — renderer/web, only a reference here, since
// recipes pull in no browser deps):
//
// import {
// ForgotPasswordScreen, SignupScreen, createLoginRoute,
// AuthShellProvider, emailPasswordClient,
// } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/web";
// import {
// RequestAccountDeletionScreen, ConfirmAccountDeletionScreen,
// defaultTranslations as deletionI18n,
// } from "@cosmicdrift/kumiko-bundled-features/user-data-rights/web";
// import { createPublicSurface } from "@cosmicdrift/kumiko-renderer/web";
//
// // AuthShell: the auth card renders inside the marketing chrome instead of
// // fullscreen. Default (without a provider) stays the fullscreen wrapper.
// const shell = ({ children }) => (
// <MarketingChrome>
// <AuthShellProvider shell={(card) => <div className="py-12 flex justify-center">{card}</div>}>
// {children}
// </AuthShellProvider>
// </MarketingChrome>
// );
//
// // createLoginRoute — do NOT render LoginScreen directly: the route
// // needs the challenge-swap logic for a second factor. Without it
// // (e.g. a raw <LoginScreen />) an MFA-enrolled user gets stuck at login
// // as soon as the app mounts auth-mfa at some point. If the app mounts
// // auth-mfa, add `mfaVerifyScreen: MfaVerifyScreen` (from
// // "@cosmicdrift/kumiko-bundled-features/auth-mfa/web").
// const LoginRoute = createLoginRoute({ loginScreenProps: { signupHref: "/signup" } });
//
// createPublicSurface({
// clientFeatures: [emailPasswordClient()], // bringt SessionProvider + i18n
// shell,
// routes: [
// { path: "/login", component: <LoginRoute /> },
// { path: "/signup", component: <SignupScreen loginHref="/login" /> },
// { path: "/forgot-password", component: <ForgotPasswordScreen loginHref="/login" /> },
// { path: "/delete-account", component: <RequestAccountDeletionScreen /> },
// { path: "/delete-account/confirm", component: <ConfirmAccountDeletionScreen /> },
// ],
// fallback: <LoginRoute />,
// });
//
//
// SERVER-WIRING: the app enables `anonymousAccess` (defaultTenantId = the
// apex host tenant) so /api/write reaches the anonymous deletion handlers:
//
// runProdApp({ features: composeApexAccountApp({...}), envSchema,
// anonymousAccess: { defaultTenantId: APEX_TENANT_ID } })
import { createAuthEmailPasswordFeature } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
import { createAuthFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
import { createDataRetentionFeature } from "@cosmicdrift/kumiko-bundled-features/data-retention";
import { createPersonalAccessTokensFeature } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant";
import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";
import {
createUserDataRightsFeature,
type SendDeletionVerificationEmailFn,
} from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
export type ApexAccountAppOptions = {
/** HMAC secret for the deletion verify token. */
readonly deletionTokenSecret: string;
/** Apex route of the confirm screen; the handler appends the token as a
* URL fragment (`#token=`). */
readonly deletionVerifyUrl: string;
/** Sending the verify magic link. MUST be non-blocking (enqueue) — a
* synchronous send would open a timing oracle for account enumeration. */
readonly sendDeletionVerificationEmail: SendDeletionVerificationEmailFn;
};
// Full feature composition for the public account flows. The
// user-data-rights options enable the anonymous deletion flow; the remaining
// features provide login/register/PW (auth-email-password) + the
// require chain (user-data-rights → data-retention + compliance-profiles +
// sessions).
export function composeApexAccountApp(opts: ApexAccountAppOptions): FeatureDefinition[] {
return [
createConfigFeature(),
createUserFeature(),
createTenantFeature(),
createAuthEmailPasswordFeature(),
createDataRetentionFeature(),
createComplianceProfilesFeature(),
createAuthFoundationFeature(),
createPersonalAccessTokensFeature({ scopes: {} }),
createSessionsFeature(),
createUserDataRightsFeature({
deletionTokenSecret: opts.deletionTokenSecret,
deletionVerifyUrl: opts.deletionVerifyUrl,
sendDeletionVerificationEmail: opts.sendDeletionVerificationEmail,
}),
];
}

📄 On GitHub: samples/recipes/apex-surface-auth/src/feature.ts