?shop= and ?user= launch parameters are
convenient, but anyone can type them into a URL.
With token exchange, your backend can also trade a session token for a normal app access
token. An embedded app then needs no OAuth redirect inside the iframe at all.
How it fits together
- Your front end calls
getSessionToken()from the App Bridge. - The dashboard asks the Salesive API to mint a token for your installation and the signed in merchant, and posts it back to your iframe.
- Your front end sends it on each request:
Authorization: Bearer <token>. - Your backend verifies it with the Salesive public key and reads the store from
dest. - Optional: the first time your backend sees a store it holds no access token for, it trades the session token for one (token exchange).
Get a token in the front end
getSessionToken() before every request rather than
storing the token yourself.
getSessionToken({ timeoutMs, forceRefresh }) rejects with a SessionTokenError whose code
is:
The cached token is dropped when the merchant logs out or switches store.
Token format
The token is a compact JWS signed with ES256 (ECDSA P-256, SHA-256).Verify a token on your backend
Fetch the public key set once and cache it:- Split the token into header, payload and signature.
- Require
algto beES256. Never let the token choose the algorithm. - Find the key whose
kidmatches. If none does, refetch the key set once (Salesive may have rotated its key), then give up. - Verify the signature over
header.payload. - Require
audto equal your client id. Salesive signs every app’s tokens with the same key, so skipping this check would let another app’s token into your backend. - Require
issto equal the Salesive API base you call. - Check
expandnbf, allowing at most a few seconds of clock drift. - Require
purposeto besession.
server/sessionToken.js):
jose:
Token exchange: install without the redirect
The merchant already approved your app’s scopes when they installed it from the marketplace. Token exchange lets your backend turn a session token into an app access token for that installation, without sending the merchant through the consent redirect again inside the iframe.400 or 401) when:
- the client secret is wrong or missing (exchange is for confidential clients only),
- the token was issued for another app (
aud), has expired, or was already exchanged (jti), - the app is no longer installed on that store.
- Exchange once per store, then refresh. Store the access and refresh tokens and only exchange when you hold none for the store (first open, or after an uninstall and reinstall). Every exchange issues a new token pair.
- Use a fresh token for the exchange (
getSessionToken({ forceRefresh: true })), since a token can be exchanged only once. - The access token acts for the store, with the scopes granted at install. It is not tied to
the user in
sub; usesubandrolefor your own user model.
Security notes
- The token is delivered only to your app’s origin (the dashboard posts it to the origin of
your
appUrl), and lives 60 seconds. - Never log full tokens, and never put them in URLs you control.
- Keep using the
?shop=parameter only as a hint for which store to show first. Decide access fromdest.
Next steps
App Bridge
The full bridge reference, including getSessionToken.
App starter
A working app with the verifier and the exchange wired in.

