SPA Components
The bundled AuthWorkflow forms are server-driven atscript types — the server decides which form to render at each pause and ships it as an annotated schema. Your SPA renders them with <AsWfForm> from @atscript/vue-wf and registers a small set of companion Vue components by name. This page covers the aooth-side contract: which component-name strings the bundled forms emit, and how to wire them. The component packages themselves (@atscript/vue-wf, @atscript/vue-aooth, @atscript/vue-form) are documented in the atscript-ui docs.
The contract: @ui.form.component '<Name>'
Three bundled form fields carry a @ui.form.component '<Name>' annotation naming a Vue component. <AsForm> (inside <AsWfForm>) resolves that string against the :components map you pass — so the string in the .as schema MUST match a key in your :components map, or the field falls back to the default renderer.
| Form field | @ui.form.component | Component (@atscript/vue-aooth) | Renders |
|---|---|---|---|
WithInlineConsentForm.consents | AsConsentArray | AsConsentArray | one checkbox per pending consent; self-hides when none pending |
SetPasswordForm.passwordRules | AsPasswordRules | AsPasswordRules | live password-policy fulfillment dots, re-evaluated per keystroke |
EnrollConfirmForm.qrCode | AsQrCode | AsQrCode | scannable TOTP otpauth:// QR + the base32 secret for manual entry |
LoginCredentialsForm.ssoProvider | AsSsoProviders | AsSsoProviders | one-click SSO provider buttons; self-hides when none configured |
All four components come from @atscript/vue-aooth (an external SPA package), not from any @aooth/* package.
Minimum wiring
<script setup lang="ts">
import { AsWfForm, AsWfFinish, type WfFinished } from "@atscript/vue-wf";
import { createDefaultTypes } from "@atscript/vue-form";
import { AsConsentArray, AsPasswordRules, AsQrCode, AsSsoProviders } from "@atscript/vue-aooth";
const types = createDefaultTypes();
// Keys here MUST match the `@ui.form.component '<Name>'` strings in the bundled forms.
const components = { AsConsentArray, AsPasswordRules, AsQrCode, AsSsoProviders };
</script>
<template>
<AsWfForm
path="/auth/trigger"
name="auth/login/flow"
:types="types"
:components="components"
:navigate="navigate"
@finished="onFinished"
@error="onError"
/>
</template>path— the/auth/triggerendpoint (theAuthControllerroute the workflow runs behind).name— the workflow id to start:auth/login/flow,auth/invite/start,auth/recovery/flow, orauth/signup/flow.:types— the built-in field renderers from@atscript/vue-form.:components— the aooth companion components, keyed by the names the forms emit.
AsQrCode — TOTP enrollment
During MFA enrollment, the EnrollConfirmForm.qrCode field carries the otpauth:// provisioning URI (built from the secret + your totpIssuer opt) and @ui.form.component 'AsQrCode'. AsQrCode renders it as a scannable SVG and extracts the base32 secret from the URI to show for manual entry (its manualSecret prop defaults on) — so there is no separate "manual secret" field. The user scans/enters it in their authenticator, then submits the 6-digit code, which the server verifies via UserService.verifyTotpSetupCode.
AsQrCode has an optional peer dependency on qrcode for the SVG render — install it in the SPA if you want the visual QR (without it, the manual secret still renders).
AsConsentArray — pending consents
When ConsentStore.getPendingConsents(subject) returns descriptors (the arg is the stable user id), the workflow transports them (via @wf.context.pass) to whichever form the user is currently on, and the inline consents: string[] field renders one checkbox per descriptor through AsConsentArray. The field self-hides when nothing is pending — no @ui.form.fn.hidden needed. Submitted ids are validated server-side against the pending set (the authoritative whitelist). See Workflows — consent collection.
AsPasswordRules — live policy readout
On SetPasswordForm, the phantom passwordRules field renders fulfillment dots that re-evaluate on every keystroke against data.newPassword, using the same transferable policy expressions the server enforces (shipped to the client via UserService.getTransferablePolicies()). See Password Policies.
AsSsoProviders — federated login buttons
When you offer federated providers (resolveAlternateCredentials().ssoProviders), the login form's LoginCredentialsForm.ssoProvider field renders them through AsSsoProviders: a one-click picker that paints each provider as a button labelled with its server-owned text ("Continue with Google"), applies the provider's icon class verbatim, and on click both selects the id and fires the field's data-carrying sso action — there is no separate submit button, and the field self-hides when no providers are configured. The selected provider rides the partial submit and AuthWorkflow.beginSso turns it into the provider 302. See Federated Login.
The icon is a CSS class you own: because the icon strings arrive from server context, a UnoCSS-style static extractor never sees them in source — safelist each one and install the matching icon collection (the demo wires i-simple-icons:google via a second presetIcons + a safelist entry). The component-API details (props, secondary chips, the "or" divider) live in the @atscript/vue-aooth docs.
Cross-flow alt-action links
The bundled forms' cross-flow navigation actions — login's signup ("Don't have an account? Sign up") and magicLink, the signup form's backToLogin ("Already have an account? Sign in"), and recovery's backToLogin ("Remembered your password? Sign in") — render as pushed-down, centered "text + link" affordances below the submit button, rather than inline above it. Three @atscript/vue-aooth / @atscript/vue-form annotations on the ui.action field drive this:
| Annotation | Effect |
|---|---|
@ui.form.pushDown | Moves the action into its own grid below the submit button (def.pushDownFields) instead of inline above it. |
@ui.form.attr 'text', '…' | Renders leading text before the link → text [link] (e.g. "Don't have an account? Sign up"). |
@ui.form.attr 'align', 'left' | 'center' | 'right' | Aligns the text+link row. The bundled cross-flow links use center. |
@atscript/moost-wf serializes all three over the workflow round-trip, so a server-driven <AsWfForm> renders them identically. The text/align attrs apply only to the standalone ui.action button (the AsAction component) — not to the inline-on-input action variant (e.g. login's forgotPassword, which sits in the password field's footer). The annotation semantics are owned by the atscript-ui docs; the bundled forms only declare them. To restyle or relabel, replace the form via opts.forms.<slot> and keep/adjust the annotations (action ids stay the routing contract — see Workflows).
Magic-link resume — initialToken
auth/recovery/flow and auth/invite/start finish their first leg by emailing a URL carrying a wfs=<token> (and, for invites, a uid=<userId>). Route that into your workflow page and pass it as :initial-token so <AsWfForm> resumes the paused state instead of starting fresh:
<AsWfForm path="/auth/trigger" :name="wfId" :initial-token="initialToken" ... />For a re-clicked invite link whose state row has already been evicted, fall through to GET /auth/invite/post-redemption?uid=<userId> and hand the returned WfFinished to <AsWfFinish> for envelope-shape parity. See REST Controllers — post-redemption.
Custom config per request — variant headers
To pick a server-side workflow preset per request, send a custom header via :fetch-options and re-key the form so it remounts when the choice changes:
const fetchOptions = computed(() =>
variant.value ? { headers: { "x-wf-variant": variant.value } } : undefined,
);The server reads the header in its AuthWorkflow subclass (e.g. in an @Injectable("FOR_EVENT") constructor) to merge a variant config. Reference: packages/e2e-demo/src/ui/pages/WfPage.vue.
Replacing a bundled form keeps the component string
If you swap a bundled form via opts.forms.<slot> (typically extends the bundled one), keep the @ui.form.component '<Name>' annotation on the field if you want the companion widget — the :components registration is keyed on the string, not the form class.
See also
- Workflows — the server side that emits these forms.
- Atscript Models — the bundled form catalogue + their annotations.
- atscript-ui docs —
<AsWfForm>,<AsWfFinish>, and the@atscript/vue-aoothcomponent APIs.