Skip to main content
The Auth Component is a pre-built React component that collects end-user credentials, stores them in the Credential Vault, and creates a Credential. Credentials never touch your systems. It handles source selection, username/password capture, interactions (MFA, security questions), and error states out of the box.
Want full customization and control over the UX? See Building your auth flow to build your own using the API directly.

How it works

  1. Your server creates a session token via POST /v2/token
  2. Your frontend mounts <DeckAuthComponent /> with the token and all configuration
  3. The component renders a secure UI (hosted at auth.components.deck.co) where the user selects a source and enters credentials
  4. Credentials go from the component directly to the Deck API and into the Credential Vault. They never touch your page
  5. On success, the component returns the new Credential ID via onSuccess

Restrictions

  • Collects credentials via username_password (default) or source_fields (opt in per source through sourceConfig). No other auth method is supported.

Installation

React 18+ is a peer dependency.

Quick example

Create a session token on your server:
Mount the component in your React app:
Client

Session token

Every auth component session starts with a server-side token. The token is authentication only. It proves the request came from your backend. All configuration (sourceId, appearance, etc.) is passed on the component, not the token.

POST /v2/token

Creates a session token for the Auth Component. Authentication: Authorization: Bearer sk_live_... Request body: None Response (201):

Token lifecycle

  • Tokens expire 30 minutes after creation
  • Expired tokens return a clear error to onError

Component props

The component does not own its lifecycle. Your app does. When onClose fires, you decide what happens (unmount, navigate, show a confirmation, etc.).

Callbacks

onSuccess

onCancel

Fires when a verification task run is canceled out-of-band (for example, you call the cancel endpoint while the run is in progress). This is not user dismissal of the component, which is onClose.

onError

type is the error category. code is the machine-readable identifier. message is human-readable and may change. Switch on type and code, not message. See Error handling for all error codes.

onClose

onClose fires when the user dismisses the flow — via the persistent close (X) icon, the Escape key, or a Cancel/Close button on the canceled or error screen. You handle the teardown. By default the X is shown whenever onClose is provided and hidden when it is omitted. To control the two independently — for example, to embed the component in your own modal that already has a close control — pass showCloseButton:
Omitting onClose entirely hides the X and leaves the Cancel/Close buttons with no handler, so prefer showCloseButton={false} when you still want those buttons to work. If a task run is in progress when the user closes the component, the task run continues running in the background. To cancel it, use the cancel endpoint from your onClose handler.

showCloseButton

Controls whether the persistent close (X) icon is rendered, independently of onClose. Defaults to onClose != null, so existing integrations are unaffected. Pass onClose together with showCloseButton={false} to hide the X while keeping the Cancel/Close buttons (which share the same close handler) functional.

Source selection

The sourceId prop controls what the user sees before credential capture.

Source fields

Some sources need values beyond username/password: an account number, a company ID, etc. The sourceConfig prop attaches source fields to a source’s credential form, keyed by source ID (or "*" as a default for any source without its own entry). By default these fields are collected alongside username/password, but a source can also authenticate on source fields alone (see Auth method). Collected values are stored on the credential under auth_credentials.source_fields; fields marked tokenized are vaulted. Only sources that need custom fields get an entry. Every other source renders the standard username/password form.
Each field is either a config object (presented to the user) or a plain string (a silent pass-through: stored on the credential, never shown).

Entry config

Each entry in sourceConfig is keyed by source ID (or "*") and takes:

Auth method

authMethod controls whether the entry’s fields sit alongside username/password or replace them. A source_fields entry must declare at least one field; one with none fails fast at mount with source_fields_auth_requires_fields, and an unknown authMethod fails with auth_method_invalid. If the source doesn’t support the selected method, the flow fails at runtime with auth_method_not_supported.

Field config

Every display string (label, placeholder, and each option label) is a LocalizedString: either a plain string, rendered as-is in every locale, or a { "en-US": string; "fr-CA"?: string } map picked by language, falling back to "en-US". The two forms can be mixed within one config.

Field validation

A string field can declare format constraints via validation. The component checks them in the form (on blur, then live as the user edits) and keeps the submit button disabled while a value violates them. The check is client-side, so it narrows what the user can submit but does not replace the source’s own validation. The keys combine: { pattern: '\\d+', max: 9 } is a digits-only field capped at 9.
Length and value bounds show a built-in message naming the bound (“Must be at least 6 characters”), localized by language. A pattern violation shows your message instead, since only you know the format the source expects, so the component supplies no default. With no message, a pattern violation still marks the field invalid and blocks submit, it just renders no text.
  • Empty values are never format-checked. required covers those, and an optional field left blank is omitted from the submission.
  • Prefills (value, or a value restored on reconnect) are checked at mount, so an invalid prefill explains the blocked submit without waiting for a blur.
  • Malformed constraints fail fast at mount with source_field_config_invalid: a pattern that isn’t a valid regex, a negative or non-integer length bound, minLength above maxLength, min above max, or validation on a type other than "string".

Behavior

  • Scoping is per source: the entry matching the selected source applies, and "*" covers any source without its own entry.
  • Stored values are always strings: selects store the option value, booleans store "true" / "false". Option values and stored values are data and are never localized.
  • Reconnect (Update Mode) prefills non-tokenized fields from the stored credential and keeps them editable. Tokenized fields render empty (their values are never returned) and are treated as optional even when required: leave one blank to keep the stored value, or re-enter it to change it, so users aren’t forced to re-enter vaulted values.
Invalid sourceConfig fails fast: the component fires onError at mount with a source_field_* code rather than erroring at credential creation. See Error handling.

Update mode

Pass a credentialId to re-collect credentials for an existing credential instead of creating a new one.

Task linking

Pass a taskId to verify credentials immediately after creation. The component fires a task run using the new credential, and the task run view stays visible while it executes. If the task declares fields in its input_schema, the component collects them from the user (or from you, silently) before firing the run. See Task input. Credential status reflects whether the agent could authenticate, not whether the run finished successfully. As long as auth succeeds, the credential becomes verified even if the run later fails. A failed run does not delete or invalidate the credential. If auth itself fails, the credential becomes invalid.
Do not rely on onSuccess with verified: false to detect a failed verification. When the run fails with an error (including an authentication failure, auth_invalid), the component fires onError, not onSuccess. onSuccess with verified: false only occurs for a run that failed without returning an error. Handle verification failures in both onError and the verified field of onSuccess.
The task you pass as taskId must actually authenticate with the source. A task that only visits a public page will not challenge the credential, and running it will not move the credential from unverified to verified. Pick (or create) a task whose first step is logging in.

taskRunView

With taskId present, the task run view is shown while the run executes:
  • "status": spinner with status text (e.g., “Connecting…”, “Verifying credentials…”)
  • "live": live view of the task run
Without taskId, no task run view is shown.

Task input

Some tasks declare input in their input_schema: a date range, an account number, a file to upload. The component supports chaining to these tasks in both auth mode and interaction mode. Whenever a taskId is provided, the component fetches the task’s input_schema at mount and presents its fields to the end user in a task details step: after credential submit in auth mode, before the run in interaction mode. The heading of the step is a generic “Additional details” (the end user doesn’t know a task is involved). The step is skipped only when the schema declares no fields, or every field is supplied silently. The optional taskInput prop refines that presentation. The schema stays the source of truth for structure: field types, required fields, enum values, and tokenization all come from the schema. taskInput supplies what the schema can’t: values you already know (passed silently, nothing rendered), localized labels and placeholders, prefills, and the step heading.
taskInput takes: Each fields entry is either a raw scalar (a silent pass-through: merged into the run’s input, never shown) or a config object (presented to the user unless hidden), the same pattern as sourceConfig. Schema fields with no entry are presented with the schema property name as their label. taskInput requires a taskId; without one the component fires onError with task_input_requires_task at mount.

Task input field config

Display strings (title, label, placeholder, option labels) are the same LocalizedString type as source fields: a plain string, or a { "en-US": string; "fr-CA"?: string } map picked by language. Unlike sourceConfig, there is no type, required, or tokenized key: those come from the schema. validation is yours to declare, because the schema states a field’s type and required-ness but not its format:
The keys and in-form behavior are the same as source field validation: pattern and the lengths check the typed text, min/max check it as a number. A malformed block fails fast at mount with task_input_config_invalid; set on a schema kind other than string or number, validation fires task_input_invalid at mount rather than being silently ignored. A value passed silently with hidden: true never reaches the form, so its constraints are checked once at mount instead.

Field rendering

The schema property’s type drives the input control: Fields the schema marks required must be filled before the user can continue, and any field carrying a validation block must satisfy it; presented optional fields left empty are omitted from the run’s input. Fields in the schema’s tokenized array render masked and are handled like credentials: never logged and never put in URL parameters. Bad task input configuration fails fast at mount, before a credential is created. See Error handling.

Interaction mode

Set mode="interaction" to collect interaction inputs (MFA code, security question, MFA method) from the user during a task run against an existing credentialId or sourceId. The component skips source selection and credential capture, triggers the task run, and renders any pending interaction in place. If the task declares fields in its input_schema, the component collects them in a task details step before triggering the run.

Required props

Interaction mode needs taskId plus one of: Both are validated before the component mounts:
  • Omitting taskId fires onError with task_id_required.
  • Providing neither a credentialId nor a single sourceId fires onError with credential_or_source_required.

Appearance

Customize the component’s look via the appearance prop.

Theme

Variables

Branding

Upload a logo in the Deck console under Settings → Branding. Deck hosts it and returns a CDN URL on content.cdn.deck.co. Pass that as logoUrl. Accepted formats are PNG, JPG, JPEG, GIF, and WebP (no SVG). The logo renders into a 40×40 box and is hidden if it fails to load.
For security, the component only loads images from Deck’s content CDN, data: URIs, and cdn.brandfetch.io. Arbitrary external URLs (for example, https://your-company.com/logo.png) are blocked by its content security policy and will not render.

Language

Set the language for all component-rendered copy via the language prop.
language applies to copy the component owns: source picker, credential capture labels and helper text, interaction prompts (generic labels and submit/continue buttons), in-UI error messages, the task run status view (taskRunView: "status"), and the close button and accessibility labels. Content returned by the source itself (source names, interaction field labels, and other source-defined strings) is not translated by the component and is rendered as the source returns it.

Interactions

Sources may require additional input mid-flow: an MFA code, a security question, an account selection. The component handles these automatically as interactions: a generic pause-and-prompt primitive. When an interaction is required:
  1. The component transitions to an interaction step and renders the fields from the source’s response
  2. The user fills in the fields and submits
  3. The source validates. On accept, the flow continues
  4. Some sources require multiple sequential interactions (e.g., MFA code then a security question)
The component does not enforce its own interaction timeout. Timeout windows are set by the source and handled by the backend. The user cannot go back to credential entry from an interaction step (credentials have already been created). See Interactions for more on how Deck models interactions.

Error handling

All errors surface through onError with a uniform { type, code, message } shape. code falls into two groups by where it comes from.

Validation errors

Raised by the component itself before any network request, from checking the token and the prop combination. These are deterministic: a given misconfiguration always produces the same code.

Runtime errors

Surfaced by the Deck API and the source as the flow runs. The component forwards the API’s type, code, and message (mapping a couple of generic not-found codes to the specific ones below). Treat this as the set you are most likely to encounter, not an exhaustive list, and handle any unrecognized code as a generic failure.

Error behavior

  • Validation errors fire before the iframe mounts. Fix the token or props and remount.
  • Task input errors (task_input_invalid, task_read_failed) fire at mount, after the task schema fetch but before any credential is created. Fix the config (or the task) and remount.
  • Token expiry (token_invalid) terminates the session. Create a new token and remount.
  • Credential and source errors (auth_invalid, account_locked, etc.) terminate the session.
  • Interaction errors (interaction_timeout) terminate the session. Wrong user input is not an error; the source emits another prompt and the user retries in place.

Embed by URL

For clients that can’t run the React SDK, load the component’s URL in a WebView (for example, in a React Native app) or an <iframe> and pass configuration as query parameters:
Each parameter does what its prop does, and the flow is otherwise the same as the React component.

URL parameters

Appearance parameter

appearance takes the same AppearanceConfig object as the prop, encoded as a URL-safe JSON string:
Malformed JSON is ignored and theming falls back to the default. For a theme-only change, use the theme shorthand instead.

Examples

Configuring via postMessage

URL parameters are subject to URL-length limits, so a large sourceConfig or taskInput (or any object config you’d rather not URL-encode) is best delivered over postMessage, which has no encoding step and no length limit. Tokenized task input values must only be sent this way. The React component uses this same handshake internally. Load the component with no configuration in the URL (just the bare origin), wait for the READY message, then post an INIT message with the full configuration object:
In a React Native WebView, run the same window.postMessage({ type: 'INIT', ... }) (via injectJavaScript) once you receive READY through onMessage.
Pick one channel. Don’t put a token in the URL if you’re going to postMessage: the component self-initializes from the URL first, and the two configurations would conflict. Configure via URL parameters or via postMessage, not both.

Receiving results

Without React callbacks, the component delivers results as messages to the client:
  • React Native WebView: messages arrive via window.ReactNativeWebView.postMessage as JSON strings. Read them with the WebView’s onMessage handler.
  • <iframe>: the same messages arrive via window.postMessage to the parent frame.
Every message the component emits is forwarded, not only the terminal results. Switch on type and ignore any you don’t handle: Each message is { type, payload }: