> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript quickstart

> Connect an enabled Authentication application to hosted sign-in with PKCE, callback validation, session checks and logout.

<Warning>
  Early access: use this hosted-runtime example only with an enabled application and its confirmed runtime URL. The `@didit-protocol/auth` package is release-dependent; the package API below is a preview, not an installable quickstart.
</Warning>

## Configure hosted sign-in

Register the exact callback URL and your web origin for your sandbox application. Obtain its runtime URL as described in the [overview](/authentication/overview). This example uses the browser's cryptography APIs and requires HTTPS, or localhost in sandbox.

The application owns `state` and the PKCE verifier. Hosted sign-in owns password, email, passkey, phone, SSO and required MFA screens. No API key belongs in this code.

```javascript auth.js theme={null}
// Replace both values with your registered application configuration.
const AUTH_BASE = "https://YOUR_AUTH_ORIGIN/auth/v2/authentication/YOUR_APPLICATION_ID/";
const REDIRECT_URI = "https://YOUR_APP_ORIGIN/auth/callback";
const PENDING = "didit.auth.pending";
let tokens = null;

function base64url(bytes) {
  return btoa(String.fromCharCode(...bytes))
    .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

export async function signIn() {
  const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
  const state = base64url(crypto.getRandomValues(new Uint8Array(32)));
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
  sessionStorage.setItem(PENDING, JSON.stringify({ verifier, state, startedAt: Date.now() }));
  const url = new URL("sign-in/", AUTH_BASE);
  url.search = new URLSearchParams({
    redirect_uri: REDIRECT_URI,
    state,
    code_challenge: base64url(new Uint8Array(digest)),
    code_challenge_method: "S256",
  }).toString();
  window.location.assign(url.href);
}

export async function handleCallback() {
  const query = new URLSearchParams(window.location.search);
  const saved = sessionStorage.getItem(PENDING);
  sessionStorage.removeItem(PENDING);
  history.replaceState(null, "", window.location.pathname);
  if (!saved) throw new Error("Start sign-in again.");
  const pending = JSON.parse(saved);
  if (query.get("state") !== pending.state || Date.now() - pending.startedAt > 600000) {
    throw new Error("Invalid or expired sign-in. Start again.");
  }
  if (query.has("error") || !query.get("code")) throw new Error("Sign-in was not completed.");
  const response = await fetch(new URL("token/", AUTH_BASE), {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "authorization_code",
      code: query.get("code"),
      code_verifier: pending.verifier,
      redirect_uri: REDIRECT_URI,
    }),
  });
  if (!response.ok) throw new Error("Code exchange failed. Start sign-in again.");
  tokens = await response.json();
  return getSession();
}

export async function getSession() {
  if (!tokens) return null;
  const response = await fetch(new URL("session/", AUTH_BASE), {
    headers: { Authorization: `Bearer ${tokens.access_token}` },
    cache: "no-store",
  });
  if (!response.ok) {
    tokens = null;
    throw new Error("Session check failed. Sign in again.");
  }
  return response.json();
}

export async function signOut() {
  const current = tokens;
  tokens = null;
  if (!current) return;
  const response = await fetch(new URL("session/", AUTH_BASE), {
    method: "DELETE",
    headers: { Authorization: `Bearer ${current.access_token}` },
  });
  if (response.status !== 204) throw new Error("Local state cleared; server logout failed.");
}
```

Call `signIn()` from your sign-in button. On the registered callback page, call `handleCallback()` once, render a loading state, and show its error if it fails. Use `getSession()` for the current user and `signOut()` for logout. The callback removes its code from the address bar before making a request.

This minimal example keeps tokens in memory and signs in again after a page reload or access-token expiry. For persistent sessions, use your backend and secure cookies or implement serialized [refresh rotation](/authentication/tokens-and-jwks). Never put refresh tokens in URLs or logs. A frontend session check controls presentation only; your backend must validate the caller on every protected request.

## Authentication SDK release preview

The planned `@didit-protocol/auth` core exposes `createDiditAuth({publishableKey})`, `signIn()`, `handleRedirectCallback()`, `getSession()`, `getToken()`, `onAuthStateChange` and `signOut()`, with `vendorData` on the identity. It is intended to add automatic device signals and a server entry point with `verifyToken()`.

Do not install a guessed version or write against guessed callback options. Confirm the published package exports, issuer and key configuration before replacing the hosted example. Password, email code, magic link, passkey, phone, MFA, social sign-in, step-up and agent methods will depend on that release's supported API.

## Add other products

Your backend maps the verified Authentication `sub` to its stable person reference. Use it as `vendor_data` when [creating a verification session](/sessions-api/create-session) or [minting a transaction token](/transaction-monitoring/sdk-transaction-submission). A successful sign-in does not imply either product approved the user.

Before shipping, exercise sign-up, returning sign-in, cancellation, mismatched state, reused code, forgot password, MFA, expiry and logout against your enabled sandbox application.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.