Step up dialog
ProA verification step before a sensitive action: the person proves it's them again, by a passkey, a fresh SSO sign-in, or a code, whichever the app provides (each shows only when its callback is given). It says what is waiting on the check, in the app's words, and that cancelling runs nothing. Nothing is chosen for the person and no method is tried on its own: the app runs the ceremony in the callback, reports a refusal through error, and closes the dialog when the check passes. Record the pass as stepup.completed in the audit log (AuditLog knows the action).
When error arrives after a code was submitted, focus moves to the code field, which rings once.
Version 5.2.0· other versions
This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).
Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1 and 2.6.2: the page came later, or had another name then.
Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.
Preview
Loading the preview
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/step-up-dialog
Brings in: bidi, button, dialog, input, locale, locked, motion, tokens, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import * as React from "react"
import { StepUpDialog } from "@/components/candor/step-up-dialog"
import { Button } from "@/components/candor/ui/button"
export default function Example() {
const [open, setOpen] = React.useState(false)
const [busy, setBusy] = React.useState(false)
const [error, setError] = React.useState<string>()
const [passed, setPassed] = React.useState(false)
const wait = () => new Promise((r) => setTimeout(r, 600))
// The app runs each check, says why one failed, and closes the dialog when one passes (and records stepup.completed).
const pass = () => {
setOpen(false)
setPassed(true)
}
return (
<div className="flex flex-col items-start gap-2 text-sm">
<Button variant="destructive" onClick={() => (setError(undefined), setOpen(true))}>
Delete the production environment
</Button>
{passed && <p className="text-muted-foreground">Checked. The demo would delete it now.</p>}
<StepUpDialog
open={open}
onOpenChange={setOpen}
action="delete the production environment"
account="sam@keel.dev"
grantFor="10 minutes"
codeHint="Enter the 6-digit code from your authenticator app. Try 123456."
busy={busy}
error={error}
onPasskey={async () => {
setBusy(true)
await wait()
setBusy(false)
setError("This device has no passkey for sam@keel.dev. Use a code instead.")
}}
onVerifyCode={async (code) => {
setBusy(true)
await wait()
setBusy(false)
if (code === "123456") pass()
else setError("That code doesn’t match. Codes change every 30 seconds.")
}}
/>
</div>
)
}<StepUpDialog>
| Prop | Type | Default | Description |
|---|---|---|---|
| openrequired | boolean | — | Whether the dialog is open. The app opens it when a step needs a fresh check. |
| onOpenChangerequired | (open: boolean) => void | — | Called with `false` when the person cancels or closes it; the step it guards does not run. |
| action | string | undefined | — | The step waiting on the check, as a verb phrase in the app's words: "delete the production environment". |
| account | string | undefined | — | Who is being checked, from the app: an email or a name. |
| onPasskey | (() => void) | undefined | — | Shows "Use a passkey"; called when it's pressed. Run the WebAuthn ceremony here. |
| onSso | (() => void) | undefined | — | Shows "Sign in again with company SSO"; called when it's pressed. |
| onVerifyCode | ((code: string) => void | Promise<void>) | undefined | — | Shows the code field; called with the code typed once it has `codeLength` digits and Verify is pressed. |
| onSendCode | (() => void) | undefined | — | Shows "Send a new code" under the code field, for a code the app sends by email or text. |
| codeHint | string | undefined | — | Where the code comes from, in the app's words: "Enter the code from your authenticator app." Replaces the general line. |
| codeLength | number | undefined | 6 | How many digits the code has. 6 unless the app says otherwise. |
| grantFor | string | undefined | — | How long the check lasts once passed, in the app's words ("10 minutes"). Left out, nothing is said about it. |
| busy | boolean | undefined | false | A check is in flight: the methods can't be pressed again and the pressed one shows a spinner. |
| error | string | undefined | — | Why the last check failed, from the app. Shown under the methods. |
| className | string | undefined | — | Extra classes for the outer element. |
Strings
The fixed words, in English by default. Change any of them under stepUpDialog in <LocaleProvider strings={{ … }}>; the table is exported as stepUpDialogStrings.
| Key | English |
|---|---|
| title | Confirm it's you |
| description | Before you {action}, check it's you. |
| descriptionGeneric | Before this step runs, check it's you. |
| account | Signed in as {account} |
| passkey | Use a passkey |
| sso | Sign in again with company SSO |
| or | or |
| code | Verification code |
| codeHint | Enter the {length}-digit code. |
| verify | Verify |
| lockedChecking | Checking it’s you |
| verifyNeedsCode | Enter all {length} digits of the code first. |
| newCode | Send a new code |
| cancel | Cancel |
| cancelNote | Cancelling runs nothing: the step waits until it's checked. |
| grant | You won't be asked again for {grantFor}. |
Motion
| Moment | Family | What it does |
|---|---|---|
| busy | live | While the app checks a code, a passkey or a sign-in, a spinner turns in the button that was pressed. It stops when the app says it is done, and under reduced motion it is still. |
| message | arrive, fade | A refusal fades in under the methods when the app reports one, and again when its text changes. One already showing when the dialog opens is still. |
Set with --cd-motion-step-up-dialog-<moment>-* in your brand file; the families are in the design reference.