# Security and Sessions

The issuer proxy authenticates to Percents and mints the CHG-scoped widget session described in [SDK Authentication](/percents-api/auth-security/sdk-auth). This page describes how to use the resulting issuer proxy session safely in the browser and iframe.

## Browser Session Contract

The issuer session endpoint returns only the browser-safe bootstrap values:

- `widgetUrl`
- `proxyApiBaseUrl`
- `proxySessionToken`
- `expiresAt`


`proxySessionToken` is an opaque, expiring credential that is valid only at the issuer proxy. It is not a Percents API token and cannot be used directly against Percents.

The issuer proxy must keep the following values server-side:

- The mapped cardholder group (`chgId`).
- The Percents widget token.
- Issuer API credentials and assertion-signing keys.


Do not include any of those values in a browser response, iframe bootstrap message, URL, client-side configuration, or log entry.

## Session Lifecycle

Create a proxy session only after authenticating the current issuer user and resolving that user or business to a persisted CHG. The proxy, not the browser, is the source of truth for that mapping.

The proxy must validate the opaque proxy-session token on every iframe request. When the session expires, the user logs out, the user's CHG access changes, or the session is suspected to be compromised, invalidate the proxy session and remove its server-side widget token. Require the host app to obtain a new proxy session before mounting or continuing the widget.

Use a shared, durable server-side session store when the proxy runs on more than one instance. An in-memory store can lose sessions on restart and cannot safely serve requests across instances.

## Iframe Boundary

The SDK mounts the widget in a sandboxed iframe. The host page supplies the opaque proxy-session token only after the iframe has loaded.

When receiving `postMessage` events, accept a message only when both conditions are true:

1. `event.origin` matches the configured Percents widget origin.
2. `event.source` is the `contentWindow` of the iframe mounted by the SDK.


Treat message data as untrusted until those checks succeed. Do not use wildcard origins or accept messages from an unexpected window. The production SDK bundle is served from a Percents-controlled origin.

## Browser Platform Controls

Configure the issuer host and proxy with these controls:

- **Iframe sandbox:** grant only the sandbox permissions required by the widget. The current SDK requires scripts, forms, same-origin, and popups; do not add permissions without a documented widget need.
- **CORS:** allow only the issuer host application's exact browser origins to call the issuer proxy. Never use `*` for authenticated proxy routes.
- **Framing policy:** configure the response `Content-Security-Policy` `frame-ancestors` directive so only the issuer's approved host origins can frame the widget.
- **Transport:** serve the host app, issuer proxy, JWKS endpoint, and widget over HTTPS in production.


## Proxy Enforcement

The issuer proxy is the browser-facing authorization boundary. For every request, it must:

1. Validate the opaque proxy session.
2. Resolve the CHG and the server-side widget token from that session.
3. Forward only the widget routes explicitly allowlisted by the issuer.
4. Send the server-side widget token to Percents; never forward the browser's proxy-session token upstream.
5. Remove upstream credentials and server-only identifiers from the browser-visible response.


Do not implement a general-purpose Percents API relay. Requests outside the widget allowlist must be rejected by the issuer proxy.