Skip to content

Authentication

Authentication

The enforcement and management APIs are authenticated by a bearer token via the api.token middleware (AuthenticateApiToken). Authentication is deny-by-default: a request with no matching token is rejected (401), and a token scoped to org A cannot act on org B (403).

Authorization: Bearer <token>

Token kinds

The ApiTokenAuthenticator contract resolves a request to an identity. The bound DatabaseApiTokenAuthenticator recognizes:

Kind How it authenticates Scope
Static operator token Matches CBOX_BILLING_API_TOKEN (config, no DB row). Any org — the simplest single-tenant / bootstrapping auth.
Per-org token Matches a row in api_tokens. One organization (or operator-wide if issued without --org).
Product-scoped token A per-org/operator token bound to one product key. Only sees and sells that product's catalog.

Leave CBOX_BILLING_API_TOKEN unset in multi-tenant deployments and issue per-org rows instead. The authenticator is a swappable contract either way.

Per-org scoping

Every management and enforcement controller calls denyUnlessMayActFor($request, $org): the token's identity must be allowed to act for the target org, or the request is refused. A product-scoped token additionally refuses another product's plan (mayUseProduct). This is how one tenant's token can never touch another's data even though both hit the same /api/v1 surface.

Issuing a token

php artisan billing:token "cbox-assistant prod" --org=<org-id> --product=<product-key>
  • name (required) — a recognizable label.
  • --org — scope to one organization id; omit for an operator-wide token.
  • --product — bind to one product key (the token only sees/sells that catalog).

The command prints the token once — store it securely; it is not recoverable.

The webhook and activation surfaces are different

Two surfaces do not use bearer tokens:

  • Payment webhooks (/webhooks/{gateway}) authenticate by the gateway signature, not a token. See Payments & dunning.
  • License activation (/api/v1/license/activate) is unauthenticated by design — the opaque deployment id is the credential. See License activation.