Skip to main content

Sessions & Authentication

Session tokens are short-lived HS256 JWTs that prove a request originated from a real merchant session inside the LaunchMyStore admin. Your app’s backend verifies them with the same clientSecret LaunchMyStore stored when the merchant installed the app — no shared session store, no round-trips back to LaunchMyStore for the common case.

Flow

The crucial property: the JWT signature is your proof. Anyone in possession of clientSecret can mint a token; the secret lives only on the LaunchMyStore host and on your backend. Your frontend never sees it.
The minting endpoint (POST /api/apps/session-token, host-internal) requires only the app apiKey plus the admin session. A domainSlug field is optional — the host resolves the shop host from the backend’s authoritative storeURL, so tokens mint correctly even when the admin session has no domainslug cookie.

Getting a Token

Vanilla SDK

App.getSessionToken() is a thin wrapper over dispatchAndWait('SESSION_TOKEN_REQUEST') — it round-trips every call and does not cache. For caching + auto-refresh-when-near-expiry, use the SessionToken helper class or the useSessionToken React hook.

React hook

Prefer getToken() over the token field. token is null until the first fetch resolves; calling getToken() always returns a valid (cached or freshly minted) token and avoids race conditions on mount.

useAuthenticatedFetch

The shortest path — a fetch wrapper that injects the Authorization header for you:

Token Structure

Authorize against the OAuth installation’s granted scopes, not the JWT permissions claim. The permissions / scopes arrays in the session token are informational. The platform itself enforces scopes server-side on every /api/v1/* call against the installation’s stored grantedScopes (presented via your OAuth access token, not the session JWT). For your own backend endpoints, authorize against the granted-scope list you persisted at install (see Authentication → OAuth), and fail closed if the installation is missing. Use the session token to authenticate identity (verify signature + aud + exp/nbf + iss, then key off sub/storeId), not to make authorization decisions from its scope claim.

Token Lifecycle

  • Lifetime: tokens expire 24 hours after issuance (exp = iat + 86400). Don’t hard-code this — always read exp from the token and refresh when it’s near expiry.
  • Caching: the SDK’s SessionToken.create(app) and the useSessionToken hook keep the token in memory until 30 seconds before expiry — call getToken() before every authenticated request and the helper does the right thing.
  • Forced refresh: call session.refresh() (vanilla) or refresh() (hook) to drop the cache and request a new token immediately. Useful after permission changes or before a long-running operation.

Verifying Tokens

Always verify on your backend with the same clientSecret you received during OAuth install.

Node.js (Express)

Use issuer: 'https://launchmystore.io' (the actual claim). Earlier docs showed issuer: 'launchmystore' — that string will fail verification.

Python (Flask)

PHP (firebase/php-jwt)

Common Patterns

Manual fetch wrapper (vanilla SDK)

React Query

Axios

Error Handling

app.getSessionToken() / session.getToken() reject with the SDK’s generic Error shape — there is no error.code. Branch on error.message if you need to distinguish causes:
Backend verification errors map cleanly:

Debugging

atob-decoding the middle segment of a JWT is safe for local inspection (it skips signature verification — never trust it for auth decisions):
Useful when a request 401s in production: log peek(token).exp - Date.now()/1000 to see if you have a clock-skew problem.

Security Best Practices

The secret lives on the LaunchMyStore host (to mint tokens) and your backend (to verify them). If you put it in JavaScript that ships to the browser, anyone can forge a token.
Every backend query that touches merchant data MUST be scoped by claims.sub. Treat session tokens like row-level security tokens.
The signature alone isn’t enough — a token issued for another app (different aud) or by a different host (different iss) is a valid JWT but a wrong one.
The session token’s permissions / scopes arrays are an informational hint, not the authorization source. Authorize against the installation’s granted scopes — the platform enforces them server-side on every /api/v1/* call (via your OAuth access token), and for your own endpoints you should check the granted-scope list you persisted at install. Fail closed if the installation is missing.
Tokens grant access to merchant data — transmit only over TLS.
The signature is private; never write tokens to logs, exception reports, or telemetry.

See Also