Skip to main content

Documentation

Entitlements & Security

How entitlement resolution works, and security caveats to review before production.

Entitlements & Security

Entitlement resolver

resolve_entitlements(app_id, customer_id, now) returns an array of EntitlementInfo:

interface EntitlementInfo {
  identifier: string
  is_active: boolean
  will_renew: boolean
  period_type: string
  latest_purchase_date: string
  expires_date: string
  grace_period_expires_date: string | null
  store: 'app_store' | 'play_store' | 'stripe'
  product_identifier: string
}

An entitlement is active when any linked subscription's current_period_end > now or its grace_period_expires_at > now:

Rendering diagram…

Security caveats

Apple JWS chain verification is rooted in Apple's Root CA - G3

apple_decode_jws_payload (in stackhouse/src/billing/validators.rs) does real cryptographic signature verification: it checks the JWS signature against the leaf certificate's public key (ES256/RS256), validates x5c chain consistency, and then pins the root to Apple's published Root CA - G3. A forged notification built with its own self-manufactured 3-cert chain (fake leaf/intermediate/root, all internally consistent) whose root is not Apple Root CA - G3 is rejected. The pinned root certificate is embedded in stackhouse/src/billing/validators.rs.

Stripe signature verification

Uses the documented t=…,v1=… scheme with constant-time comparison and a 5-minute clock-skew tolerance.

Google Play validation

Requires you to mint the OAuth2 service-account token yourself; pass it per-request or via STACKHOUSE_BILLING_GOOGLE_ACCESS_TOKEN.