The SDK does not authenticate an end user itself. The issuer authenticates its user in its own backend, then gives the SDK a short-lived session through an issuer-controlled proxy.
Production SDK integrations use signed JWT client assertions. The issuer proxy signs every widget-session mint request with its private key, and Percents verifies that assertion with the issuer's registered JWKS. This is the required production authentication method; do not use a shared secret or issuer API token for this flow.
Issuer host app and browser
-> issuer proxy: opaque proxy session token
-> Percents: no issuer credential and no widget token
Issuer proxy
-> Percents: short-lived, CHG-scoped widget tokenThe browser and hosted iframe must never receive an issuer-wide Percents API token or the Percents widget token. The opaque proxy session token is meaningful only to the issuer proxy.
- The issuer backend authenticates the current user with its existing identity system.
- The issuer backend resolves that user or business to a persisted Percents cardholder group (
chgId). Do not accept a client-side-suppliedchgId. - The issuer proxy obtains a short-lived widget token from Percents, scoped to that CHG, and stores it server-side.
- The proxy creates an opaque, expiring proxy session and returns its token, the proxy API URL, and the widget URL to the SDK.
- The hosted iframe sends its requests to the issuer proxy with the proxy-session token. The proxy validates the session, forwards only widget-allowed operations with the scoped widget token, and removes server-only data from responses.
Persist the issuer user/business-to-CHG mapping. Percents webhooks identify the CHG, so the issuer needs that durable mapping to route follow-up work or notifications correctly.
For integration setup and the session response shape, see the SDK Quickstart. For iframe message and session boundaries, see SDK Security and Sessions.
The issuer proxy authenticates itself to Percents by sending a signed JWT assertion with every widget-session mint request. The assertion is a short-lived proof that the issuer proxy controls its registered private key and is authorized to mint a session for one cardholder group.
- The issuer host app asks its own backend for a widget proxy session. Its existing issuer login session authenticates this request.
- The issuer proxy authenticates the user and resolves the user or business to a persisted Percents cardholder group (
chgId). Never accept a client-side-suppliedchgIdas the source of truth. - The proxy creates a new signed mint assertion using its private key. The private key stays in issuer-controlled server-side infrastructure.
- The proxy sends the assertion and
chgIdto Percents to mint a widget session. - Percents selects the issuer's public key using the assertion's
kid, retrieving the issuer's JWKS as needed. - Percents validates the assertion's signature, algorithm, issuer, audience, expiry, issue time, unique ID, and ownership of the requested CHG.
- Percents returns a short-lived widget token scoped to that CHG and widget-safe operations. The proxy stores this token server-side.
- The proxy returns an opaque proxy-session token to the SDK. The browser and iframe use that token only with the issuer proxy; they never receive the signed assertion, private key, issuer API token, or Percents widget token.
The issuer proxy mints a Percents widget session with:
POST <percents-api-base-url>/api/v2/widget/session
Authorization: Bearer <signed-mint-assertion>
Content-Type: application/json
{
"chgId": "chg_..."
}<signed-mint-assertion> is an RS256 JSON Web Signature (JWS) carrying a JSON Web Token (JWT). It is sent only from the issuer proxy to Percents.
The following fields are required for every signed mint assertion:
| Location | Field | Value | Why Percents validates it |
|---|---|---|---|
| Protected header | alg | RS256 | Ensures Percents uses the configured asymmetric signing algorithm and never accepts an unexpected algorithm. |
| Protected header | kid | Identifier for a public key in the issuer's JWKS | Lets Percents select the correct public key and supports safe key rotation. |
| JWT claim | iss | Issuer identifier supplied by Percents during onboarding | Identifies the issuer making the assertion. Percents derives the issuer from this verified claim; do not send a separate issuer identifier in the request body. |
| JWT claim | chgId | The Percents cardholder group being authorized | Binds the assertion to one CHG. It must exactly match the request body and belong to the issuer identified by iss. |
| JWT claim | aud | Exact audience value supplied by Percents for this issuer and environment | Identifies the intended recipient: the Percents SDK-session endpoint. It prevents an assertion for another API or environment from being reused here. |
| JWT claim | iat | Time the assertion was issued | Allows Percents to reject assertions that are too old. |
| JWT claim | exp | Expiration time, no more than five minutes after iat | Limits the time in which a stolen assertion could be used. |
| JWT claim | jti | A new, unique assertion ID, such as a UUID | Allows Percents to reject a replay of an assertion that has already been used. |
aud is the standard JWT audience claim defined in RFC 7519 section 4.1.3. It identifies the intended recipient of a token. In this flow, the recipient is the Percents SDK-session endpoint—not the issuer, the SDK, or the iframe.
Percents supplies the exact, environment-specific audience value during onboarding. The issuer proxy must include that exact value in every mint assertion, and Percents rejects an assertion with any other value. This prevents an assertion created for another API, customer integration, or environment from being accepted by the widget-session mint endpoint. This follows the JWT best practice to validate issuer and audience claims (RFC 8725 section 3.9).
- Obtain the issuer identifier and expected audience from your Percents account manager.
- Generate and protect an RS256 private key in issuer-controlled server-side infrastructure. Do not expose it to a browser, mobile app, or client-side configuration.
- Publish the corresponding public key in a stable HTTPS JWKS endpoint, typically
https://issuer.example.com/.well-known/jwks.json. - Provide Percents with that JWKS URL. Percents uses it to verify assertions from the issuer proxy.
- When rotating keys, publish the new public key with a new
kidbefore the proxy begins signing with its corresponding private key. Keep the prior public key available until all assertions signed with it have expired.
Percents resolves the public key selected by kid, verifies the signature and claims, rejects a jti that has already been used during the assertion's validity window, and confirms that the CHG belongs to the issuer identified by iss. Percents then returns a short-lived widget token restricted to that CHG and the widget-safe operations.
JWKS discovery supports signing-key rotation without sharing a secret or manually registering a new key.
- Keep issuer API tokens, signing keys, and widget tokens in server-side infrastructure only.
- Use an expiring, opaque proxy session token in the browser; validate it on every proxy request.
- Allowlist only the widget routes the proxy needs. Do not turn the proxy into a general-purpose Percents API relay.
- Enforce the server-resolved CHG on every proxied request and remove
chgIdfrom browser-visible responses. - Store proxy sessions and widget tokens in a shared, durable server-side store when using multiple proxy instances.
- Restrict browser origins and iframe framing with appropriate CORS and CSP
frame-ancestorsconfiguration.