# Embedded SDK (web message)

Truora Pass provides a lightweight JavaScript SDK, `TruoraPass`, that runs the authorization flow inside a modal on your own page instead of redirecting the user's browser away from your application. Truora Pass opens in an embedded iframe and delivers the authorization code back to your page through a browser `postMessage` event, using `response_mode=web_message`.

The result is the same as the [Authorization Code flow](/truora-pass/authorization_code_flow/): your application receives an authorization `code`, and your backend exchanges it for tokens. Only the front-channel changes — there is no full-page redirect.

## Registration requirements

The embedded flow must be enabled for your application before you can use it. You enable it yourself in the Truora Pass Dashboard, when creating or editing your application:

- **Enable JavaScript Client** (`allows_web_message`): turn the toggle on.
- **Authorized JavaScript origins** (`allowed_web_message_origins`): the list of web origins allowed to embed Truora Pass (origin only — scheme + host, no path). The origin of the `redirectUri` you pass to the SDK must be in this list, or the authorization is rejected.

See [Registering your application](/truora-pass/registering_your_application/) for the dashboard walkthrough and the full list of configurable fields.

## 1. Load the SDK

Load `client.js` from the Truora Pass host with a script tag:

```html
<script src="https://pass.truora.com/client.js"></script>
```

The script exposes a global `TruoraPass` object.

## 2. Initialize with TruoraPass.init

Call `TruoraPass.init` once, before any authorization attempt:

```javascript
TruoraPass.init({
  appClientId: "WLT_APP_your_client_id",
  onAuthorized: function (result) { /* { code, state } */ },
  onCancelled: function (result) { /* { state } */ },
  onError: function (result) { /* { error, error_description, state } */ },
  onClose: function () { /* modal closed */ }
});
```

| Option | Required | Description |
|---|---|---|
| `appClientId` | Yes | Your application's `client_id` (`WLT_APP_...`). `init` throws if it is missing. |
| `baseUrl` | No | The Truora Pass host. Defaults to production (`https://pass.truora.com`). |
| `onAuthorized` | No | Called with `{code, state}` when the user completes authorization. |
| `onCancelled` | No | Called with `{state}` when the user cancels. |
| `onError` | No | Called with `{error, error_description, state}` when authorization fails. |
| `onClose` | No | Called when the modal is closed. |

**Note**: `baseUrl` can simply be omitted — it defaults to `https://pass.truora.com`, the same host you load `client.js` from.

## 3. Start the flow with TruoraPass.authorize

```javascript
TruoraPass.authorize({
  redirectUri: "https://app.example.com/callback",
  scope: "openid profile email",
  state: "<unguessable random value>"
});
```

| Parameter | Description |
|---|---|
| `redirectUri` | A registered redirect URI. Its origin must be in `allowed_web_message_origins`. |
| `scope` | Space-delimited scopes to request. See the [Scopes reference](/truora-pass/scopes_reference/). |
| `state` | An opaque random value your page generates. Echoed back in every callback — verify it matches. |

`TruoraPass.authorize` returns nothing (`void`). The outcome is delivered exclusively through the `init` callbacks. It throws if `TruoraPass.init` was not called first.

Under the hood, the SDK opens the Truora Pass `/authorize` page in an iframe with `response_type=code` and `response_mode=web_message`, passing your `client_id`, `scope`, `state`, and `redirect_uri`.

## 4. Receive the result

Truora Pass posts the result to your page as a message. The SDK verifies that the message comes from the Truora Pass origin and from its own iframe, invokes the matching callback exactly once, and then closes the modal automatically:

| Event | Callback | Payload |
|---|---|---|
| `wallet:authorized` | `onAuthorized` | `{ code, state }` |
| `wallet:cancelled` | `onCancelled` | `{ state }` |
| `wallet:error` | `onError` | `{ error, error_description, state }` |

## 5. Exchange the code server-side

The `code` in the `wallet:authorized` payload is an authorization code — it is not a token. Send it to your backend and exchange it there, exactly as in the redirect flow: `POST https://api.pass.truora.com/v1/oauth2/token` with your `client_secret`. Never exchange the code in the browser: the exchange requires your `client_secret`, which must never be exposed in frontend code.

The exchange request, response, and error handling are identical to the [Authorization Code flow](/truora-pass/authorization_code_flow/) — including that the code is single-use and short-lived.

## Modal behavior

- On desktop, Truora Pass opens as a centered 440×680 modal over a fixed page overlay.
- On viewports 640px wide or narrower, it opens as a bottom sheet covering 90% of the viewport height.
- The embedded iframe is allowed to use the camera and WebAuthn, so the user can complete document capture, face verification, and passkey sign-in without leaving your page.
- Once a callback fires, the modal closes automatically.

## Complete example

A minimal page that signs a user in with Truora Pass and forwards the code to its own backend:

```html
<button id="truora-pass-signin">Sign in with Truora Pass</button>

<script src="https://pass.truora.com/client.js"></script>
<script>
  // Generate an unguessable state and keep it to verify the response
  const state = crypto.randomUUID();

  TruoraPass.init({
    appClientId: "WLT_APP_your_client_id",

    onAuthorized: function (result) {
      if (result.state !== state) {
        console.error("state mismatch — discarding response");
        return;
      }

      // Exchange the code on YOUR server (never in the browser)
      fetch("/truora-pass/callback", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ code: result.code })
      });
    },

    onCancelled: function (result) {
      console.log("user cancelled authorization", result.state);
    },

    onError: function (result) {
      console.error("authorization failed:", result.error, result.error_description);
    },

    onClose: function () {
      console.log("Truora Pass modal closed");
    }
  });

  document.getElementById("truora-pass-signin").addEventListener("click", function () {
    TruoraPass.authorize({
      redirectUri: "https://app.example.com/callback",
      scope: "openid profile email",
      state: state
    });
  });
</script>
```

Your backend endpoint (`/truora-pass/callback` above) performs the token exchange and then reads the user's identity from `/v1/oauth2/userinfo` — see [UserInfo and claims](/truora-pass/userinfo_and_claims/).

**Note**: The example generates `state` in the browser for brevity. For high-risk applications, follow the same practice as the [Authorization Code flow](/truora-pass/authorization_code_flow/): generate `state` on your server, bind it to the user's session, and verify it server-side when the `code` arrives — a browser-only check does not protect a compromised page.
