Skip to content

Secure JWT handshake

GET https://dev.sahl-ai.pages.dev/jwt-auth-secure

Use this mode with PKI authentication. Your page sends the signed assertion to the panel. The panel exchanges it for a session token itself, so your backend never calls the exchange endpoint.

1. Embed the panel

<iframe id="sahl-iframe"
        src="https://dev.sahl-ai.pages.dev/jwt-auth-secure?theme=<your-theme>"
        allow="microphone; display-capture"></iframe>

Only theme goes in the URL. Leave it out if Sahl AI has not given you one.

2. Answer the handshake

const SAHL_ORIGIN = "https://dev.sahl-ai.pages.dev";
const sahlFrame = document.getElementById("sahl-iframe");

window.addEventListener("message", (event) => {
  if (event.origin !== SAHL_ORIGIN) return;
  if (event.source !== sahlFrame.contentWindow) return;

  if (event.data?.type === "SAHL_IFRAME_READY") {
    sahlFrame.contentWindow.postMessage(
      {
        type: "SAHL_IFRAME_INIT",
        payload: {
          token: SIGNED_ASSERTION, // required: note the key is "token"
          px_id: "PATIENT_ID",
          ex_id: "ENCOUNTER_ID",
          ux_id: "USER_ID",
          age: 3652, // age in days (10 years)
          gender: "male",
          cta: "Send to HIS"
        }
      },
      SAHL_ORIGIN
    );
    return;
  }

  if (event.data?.type === "AUTH_ERROR") {
    console.error("Sahl authentication failed:", event.data.error);
    return;
  }

  handleSahlData(event.data);
});

Differences from the access-token handshake

Secure handshake Secure JWT handshake
Entry point /iframe-secure /jwt-auth-secure
Credential key in payload access_token token
Credential Access token from /users/login Signed assertion
On failure Error shown in the panel Error shown in the panel and AUTH_ERROR posted to your page

Rules

  • One assertion per launch. Each assertion can be exchanged only once. Sign a new one every time you load or reload the panel. Follow the claim rules in Signed JWT: exp no more than 5 minutes ahead, a current iat and a fresh jti.
  • Reply within 10 seconds of SAHL_IFRAME_READY.
  • Reloads: set src back to /jwt-auth-secure?theme=... and answer the new READY with a new assertion.
  • No context in the URL: px_id, ex_id, ux_id, age, gender and cta go in the payload only.
  • Write-back messages go only to the origin that completed the handshake.

Errors

When the panel cannot authenticate, it posts:

{ "type": "AUTH_ERROR", "error": "<human-readable reason>" }

Log it or show it. Common causes are an expired or reused assertion, a kid that has not been registered, or a clock that is out of sync.