An embedded app is your own web app shown inside Clarion, under Apps in the sidebar. Clarion passes it a short-lived signed token that says which company and person is viewing. This page covers adding the app and building the three security layers it needs. For the overview, see the embedded apps integration page.
Add the app in Clarion
You need to be an owner or admin.
- Go to Settings → Integrations and scroll to Embedded Apps.
- Click Add App.
- Enter a name (it’s the sidebar label) and the app’s URL. It must start with
https://. - Click Add App. It appears under Apps in the sidebar for everyone in the company.
- Click Secret next to the app and copy the signing secret (
clrsec_…). Your server verifies tokens with it.
To take an app out of the sidebar, click Remove.
You’ll also need the app’s id, which tokens carry in their aud claim. It’s the last part of the app’s address in Clarion: https://app.onclarion.com/apps/<app id>.
1. Lock framing to Clarion
Send this response header on every page of your app, so browsers refuse to frame it anywhere else:
Content-Security-Policy: frame-ancestors https://app.onclarion.com
2. Ask Clarion for context in the browser
When your page loads, tell the parent window you’re ready, then pass the token it sends back to your own backend. The signing secret must never reach the browser.
const CLARION_ORIGIN = "https://app.onclarion.com";
// tell Clarion we're ready for context
window.parent.postMessage({ type: "clarion:ready" }, CLARION_ORIGIN);
window.addEventListener("message", async (e) => {
if (e.origin !== CLARION_ORIGIN) return; // only trust Clarion
if (e.data && e.data.type === "clarion:context") {
// relay to YOUR backend, which holds the secret and verifies
const r = await fetch("/clarion-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: e.data.token }),
});
r.ok ? renderApp(await r.json()) : showLockedState();
}
});
Clarion only answers clarion:ready messages from your app’s exact origin, and only sends the token back to that origin.
3. Verify the token on your server
The token is a compact HS256 JWT signed with your app’s secret. Verify it before trusting anything in it:
// Node.js — keep CLARION_SECRET server-side only
const crypto = require("crypto");
function verifyClarionToken(token, secret, appId) {
const [h, p, sig] = token.split(".");
if (!h || !p || !sig) throw new Error("malformed token");
const expected = crypto.createHmac("sha256", secret)
.update(h + "." + p).digest("base64url");
const a = Buffer.from(sig), b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b))
throw new Error("bad signature");
const claims = JSON.parse(Buffer.from(p, "base64url").toString());
if (claims.iss !== "clarion") throw new Error("bad issuer");
if (claims.aud !== appId) throw new Error("token not for this app");
if (claims.exp * 1000 < Date.now()) throw new Error("expired");
return claims; // { companyId, teamId, userId, email, name, role, ... }
}
Any standard JWT library that supports HS256 works too, as long as you check iss, aud and exp.
The claims
| Claim | Meaning |
|---|---|
iss | Always clarion |
aud | This app’s id |
companyId, companySlug, companyName | The company the viewer is in |
teamId | The app’s team, or null for a company-wide app |
userId, email, name | The viewer |
role | The viewer’s role in the company: OWNER, ADMIN, INTERNAL, ADVISOR or CONTRACTOR |
iat, exp | Issued and expiry times; tokens last 5 minutes |
Scope everything your app shows to the verified companyId and role. If no valid token arrives, the app wasn’t opened from Clarion: show a locked state and do nothing else. Since tokens expire after five minutes, have your backend create its own session once the token checks out.
Reading Clarion data
The token proves identity; it doesn’t grant API access. To read or write Clarion data, call the API from your backend with a company API key — for example, the read endpoints with metrics:read.
Building with an AI assistant
Under Securing your app (so it only runs inside Clarion) in the Embedded Apps card, Copy guide for LLM copies the handshake, token shape and all three layers as a single Markdown prompt.