Alice → your backend
Your existing login identifies Alice. Your backend selects her user ID and checks whether she may claim this purchase.
Authentication answers “who is making this request, and what may they do?” There are two separate relationships: Alice signs in to your app; your backend authenticates itself to the commerce provider.
Your existing login identifies Alice. Your backend selects her user ID and checks whether she may claim this purchase.
A server credential allows account reads and changes. Keep it on your backend; Alice’s app must never receive it.
A receipt proves neither relationship. Bob holding Alice’s receipt must not be enough to move the purchase to Bob.
Two credentials, with the same boundary
openiap-commerce-protocol-example
Two fixed demo credentials separate verification from account operations. The request handler checks the role before parsing input. The dashboard preselects Alice; it is not an app login implementation.
Read code: fetch: authorization before inputPublishable and secret keys identify the calling project. Convex checks the stored key’s permissions before server operations. Your own backend still authenticates Alice and chooses her user ID.
Read code: commerceAuth / authoritativeServerAuthExample. The request checks reject missing credentials and the verification credential on account operations. Run npm test. Inspect reference
IAPKit. Tests exercise missing, invalid, and under-scoped keys before input parsing on both bindings. Inspect reference
A verification credential can check purchase evidence. A server credential can also read access, bind purchases, and request erasure. These must be different credentials.
| Role | Holder | May call |
|---|---|---|
verification | May ship inside an application | providerCapabilities, verifyPurchase |
server | The caller's authenticated backend | providerCapabilities, subscriptionStatus, entitlements, verifyPurchase, bindPurchase, eraseUser |
One operation needs no credential at all: providerCapabilities is a public read with no customer purchase data (auth role none). Every other operation requires one of the two roles above.
Providers issue their own credentials; the protocol does not impose a key format. Credentials travel in the Authorization header and never in a URL. A protected request without a credential returns UNAUTHORIZED; a credential with the wrong role returns FORBIDDEN. Separate credentials keep a shipped app from looking up or changing other users’ accounts. Full rules: SPEC.md §5.
Authenticate server-role operations before validating their input. In GraphQL this includes authorization before variable coercion, not only inside a resolver. The app backend selects the user from its session and ownership policy; neither a client-supplied user ID nor possession of a receipt authorizes binding by itself.