UnifyIDDeveloper

Embedded integration

UnifyID component

Place a Continue with UnifyID control inside any web application while retaining the Hosted UnifyID security boundary.

Versioned release

1. Load and configure

<script
  src="https://developer.dev.unifyid.io/sdk/unifyid-connect/v1.2.1/unifyid-connect.js"
  integrity="{sha384_from_release_manifest}"
  crossorigin="anonymous"
  defer>
</script>

<unifyid-continue
  client-id="vid_sandbox_..."
  redirect-uri="http://localhost:4000/callback"
  scopes="openid profile email identity_verified"
  purpose="Create and secure your account"
  authorize-url="https://api.dev.unifyid.io/v1/oauth/authorize"
  mode="popup">
</unifyid-continue>

Pin the complete versioned URL in Production and copy its SHA-384 value from /sdk/unifyid-connect/manifest.json. The latest channel is intended only for evaluation because it can change without an application deployment.

The component opens a centered desktop window immediately from the person's click, then navigates it to Hosted UnifyID. It generates state, nonce, and S256 PKCE values, uses redirect mode on mobile, and falls back to redirect when a popup is unavailable. The Client ID is public; never place a Client Secret in browser code.

Component attributes

AttributeRequiredDefaultContract
client-idYesNoneEnvironment-specific public Client ID from the application.
redirect-uriYesNoneExact registered HTTP localhost or HTTPS callback URI.
scopesNoopenidSpace-separated scope names enabled for the application.
purposeNoApplication sign-in purposePlain-language consent purpose displayed by Hosted UnifyID; maximum 200 characters.
authorize-urlYes, unless globally configuredNoneHTTPS UnifyID endpoint ending in /v1/oauth/authorize. Localhost HTTP is allowed locally.
modeNopopuppopup or redirect. Popup automatically becomes redirect on mobile and narrow screens.
labelNoContinue with UnifyIDVisible accessible button label. Set localized text appropriate to the page language.
disabledNoAbsentBoolean attribute that prevents authorization and disables the internal button.

Global configuration and telemetry

UnifyID.configure accepts an optional default authorizeUrl and an optional synchronous onEvent(event) observer. It returns the active URL and SDK version. Events are observational and contain no authorization code, token, verifier, claim, or personal information.

window.addEventListener("DOMContentLoaded", () => {
  UnifyID.configure({
    authorizeUrl: "https://api.dev.unifyid.io/v1/oauth/authorize",
    onEvent(event) {
      // { name, version, timestamp, mode? }
      sendBoundedMetric(event);
    }
  });
});

With a static defer script, configure inside DOMContentLoaded or from a later deferred script. When loading dynamically, call configure from the script element's load event. Do not call it before the SDK has created window.UnifyID.

sdk.configuredauthorization.startedauthorization.createdauthorization.redirectedauthorization.completedauthorization.cancelledauthorization.failedauthorization.timeout

authorization.started precedes transaction creation. Exactly one terminal event should follow a popup attempt: completed, cancelled, failed, or timeout. Redirect navigation ends the current page lifecycle after authorization.redirected, so terminal completion is observed by the callback application rather than the original document.

2. Complete and exchange inside the popup

try {
  await UnifyID.completePopupCallback({
    exchangeUrl: "/api/auth/unifyid/exchange"
  });
  // The backend creates the session before the popup closes.
} catch {
  // Show a bounded retry message inside the popup.
}

3. Continue in the original page

const control = document.querySelector("unifyid-continue");
control.addEventListener("unifyid:success", ({ detail }) => {
  if (!detail.completed) return;
  window.location.assign("/dashboard");
});
control.addEventListener("unifyid:error", ({ detail }) => {
  // Show a recoverable error without logging tokens.
});
control.addEventListener("unifyid:cancel", () => {
  // Keep the person on the current page.
    });

Custom events

All component events bubble and are composed, so listeners attached to the custom element or an ancestor can receive them outside the Shadow DOM.

EventDetailMeaning
unifyid:success{ completed: true, state }Recommended secure-popup flow. The callback backend exchanged the code and created the application session before signaling completion.
unifyid:success{ completed: false, code, state, nonce, codeVerifier, redirectUri }Advanced code-transport flow. Send these values immediately to the same-origin backend and never log or persist them in browser storage.
unifyid:error{ code, message, error }Configuration, popup, timeout, callback, or exchange failure. error remains as a compatibility alias for message.
unifyid:cancel{ reason: "user_closed", message }The person closed or cancelled the popup. Keep the current page usable and allow another attempt.

The recommended flow produces completed: true. The explicit completed: false variant exists only for applications that intentionally receive the authorization code in the opener. It is not an intermediate loading state.

Framework integration

React and Next.js

Render the custom element from a Client Component and attach typed custom-event listeners through a ref. This works consistently across React versions and avoids treating custom event names as React props. Do not read window.UnifyID during server rendering.

"use client";
import { useEffect, useRef } from "react";
import "@unifyid/connect";

export function ContinueWithUnifyID() {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    const element = ref.current;
    const success = (event: Event) => {
      const detail = (event as CustomEvent).detail;
      if (detail.completed) window.location.assign("/dashboard");
    };
    element?.addEventListener("unifyid:success", success);
    return () => element?.removeEventListener("unifyid:success", success);
  }, []);
  return <unifyid-continue ref={ref} client-id="..." redirect-uri="..." />;
}

For Next.js, place the import and element in a file containing "use client". The element's light-DOM markup is stable during SSR; registration and Shadow DOM creation happen only in the browser, preventing server access to Custom Elements APIs.

Vue

// vite.config.ts
vue({
  template: {
    compilerOptions: {
      isCustomElement: tag => tag === "unifyid-continue"
    }
  }
})

// Component template
<unifyid-continue
  client-id="..."
  redirect-uri="..."
  @unifyid:success="handleSuccess" />

Vue attributes are strings. Use disabled as a boolean attribute and listen to the native custom events rather than wrapping the component's internal button.

Styling and accessibility

The component uses an open Shadow DOM. Style its internal button with the exposed button CSS part. Internal markup is not a public styling contract.

unifyid-continue::part(button) {
  min-height: 48px;
  border-radius: 6px;
  font: 600 14px Montserrat, system-ui, sans-serif;
}

The internal control is a native button, supports keyboard activation, exposes disabled and aria-busy, and restores focus to itself after popup completion, cancellation, timeout, or failure. Use a descriptive localized label. Do not remove the visible focus indicator in application CSS. Hosted sign-in, consent, and face capture own their own focus management and language.

Content Security Policy

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://developer.dev.unifyid.io;
  connect-src 'self' https://api.dev.unifyid.io;
  base-uri 'none';
  frame-ancestors 'none';
  object-src 'none';

The component uses a top-level popup or redirect and is not embedded in a frame. If your browser enforces navigate-to, allow the UnifyID authorization origin. The versioned script uses crossorigin="anonymous" for integrity checking; the UnifyID asset response therefore includes Access-Control-Allow-Origin: * and Cross-Origin-Resource-Policy: cross-origin. Keep token exchange on your same-origin backend.

Browser support

BrowserSupport policy
Chrome and EdgeLatest two stable desktop releases; current Android Chrome.
FirefoxLatest two stable desktop releases.
SafariLatest two stable macOS releases; iOS and iPadOS Safari 16.4 or later.

Required platform features are Custom Elements, Shadow DOM, Web Crypto, sessionStorage, URL, postMessage, and popup support. Embedded webviews are not supported unless they provide those APIs and preserve secure system-browser authorization behavior.

npm and TypeScript

The release contains the publishable @unifyid/connect package with global element, event, telemetry, configuration, and result declarations. Importing it registers the component.

npm install @unifyid/connect@1.2.1

import "@unifyid/connect";
import type {
  UnifyIDAuthorizationResult,
  UnifyIDErrorDetail,
  UnifyIDTelemetryEvent
} from "@unifyid/connect";

Until the package is visible in your configured npm registry, use the versioned script distribution. Do not replace a pinned release with an unversioned repository file.

Component boundary

The component never receives passwords, biometric images, provider payloads, face scores, access tokens, or confidential client secrets. The parent validates the callback origin, popup window, state, and message type. In the recommended flow, authorization codes and PKCE verifiers go directly from the callback to the same-origin application backend and are not sent through postMessage.

Backend exchange

Your exchange endpoint exchanges the code, validates the issued tokens, creates an HTTP-only application session, and returns a bounded success response. Then retrieve UserInfo as described in the Quickstart.

Operational release controls

01
Pin the release

Use an explicit component version and its matching integrity hash in every Production deployment.

02
Keep telemetry bounded

Capture lifecycle events through UnifyID.configure({ onEvent }). Never attach tokens, authorization codes, claims, or personal information.

03
Monitor the journey

Track authorization starts, completions, cancellations, failures, and timeouts without recording identity data.

04
Preserve rollback

Retain the previously approved version so rollback requires only the earlier script URL and integrity value.

05
Verify before promotion

Run popup and redirect journeys across every supported browser before promoting a component release.

Was this page helpful?
UnifyID Developer Documentation · Version V.1 · Updated July 2026