Skip to content

feat(socket-auth): storefront customer and checkout socket tokens, channel resolvers - #113

Open
roncodes wants to merge 1 commit into
mainfrom
feature/socket-auth
Open

roncodes wants to merge 1 commit into
mainfrom
feature/socket-auth

Conversation

@roncodes

@roncodes roncodes commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

What

Storefront's part of realtime socket authentication: it mints the two principals Storefront owns and authorizes the two channel prefixes it owns.

  • POST storefront/v1/customers/socket-token: authenticated like the other customer endpoints (storefront key plus the Customer-Token header). It mints a customer token: sub and ids come from the customer contact (uuid, public_id), cid/cpid from the storefront's company, sid is the store or network uuid, and env is live because storefront keys have no test mode. The response is {token, expires_in, expires_at}. It returns 404 while socket auth is disabled (SocketToken::enabled() is false). It returns 401 when there is no authenticated customer, or when the customer does not belong to the storefront key's company.
  • Checkout initialization (GET storefront/v1/checkouts/before, which the SDK calls checkout.initialize): when socket auth is enabled, the cash, Stripe and QPay responses gain socket_token. So does the Stripe payment-intent update, which also creates a new checkout. socket_token is {token, expires_in, expires_at} for a checkout principal: sub is the checkout uuid, scp is ["checkout.<checkout public_id>"], plus cid/cpid and the store or network as sid. A guest can therefore listen to its own checkout and nothing else. The field is absent while socket auth is disabled, so existing response shapes are unchanged.
  • Channel resolvers, registered with core-api's SocketChannelRegistry from the service provider:
    • storefront.{id}: id is a store or network uuid, public_id or key. Users and API credentials are allowed when it belongs to their company. A customer is allowed only for the storefront named in its token's sid, within its company. Every other kind is denied.
    • checkout.{id}: id is a checkout uuid or public_id. Users and API credentials are allowed when it belongs to their company. A customer is allowed only for checkouts it owns. Every other kind is denied; a checkout token's scp already limits it to its own channel.
  • QPay capture publish: the channel name stays checkout.{public_id}, but the payload is now {checkout, status, order, error} instead of the raw QPay payment row:
    • status is one of paid, completed or failed.
    • order is serialized as GET checkouts/status returns it, and is null on failure.
    • storefront-app's use-qpay-checkout reads { order, error } from this event and passes order straight to the Order screen, so it gets a usable order.
    • A publish or serialization failure is now logged and swallowed, so it can no longer turn a recorded payment into an error response.
    • The callback's own HTTP response (respond=1) is unchanged.

Why

The socket server will start authorizing subscriptions (see the realtime socket-auth design). Storefront clients need a way to get tokens, and the socket server needs resolvers for the prefixes Storefront owns.

Dependency

Requires fleetbase/core-api ^1.6.69, the release that carries SocketToken, SocketPrincipal and SocketChannelRegistry; composer.json is raised accordingly. This PR's CI cannot pass until that core-api release is tagged. No version bump here; the release branch does that.

Test plan

Verified by CI (composer test:unit and the coverage baseline). New and updated tests cover:

  • the socket-token route: 404 when disabled; 401 without a customer, with an unknown token, without a storefront, or across companies; claims verified with SocketToken::verify for store and network keys
  • socket_token absent/present on an initialized checkout, with its scp, sid (store, or network fallback) and cpid
  • storefront/checkout resolvers: lookup by uuid, public_id and key; cross-company denial; customer narrowing by sid and ownership; other kinds denied; registration through the provider
  • QPay publish payload shapes (completed, paid, failed), order serialization matching checkout status, and a failed publish not failing the callback

API / docs impact

  • New endpoint POST storefront/v1/customers/socket-token, and a new optional socket_token response field on checkout initialization. fleetbase/postman and the storefront API docs on fleetbase/fleetbase.io need entries (follow-up).
  • The checkout.{id} realtime event payload changed shape, as described above.

Related PRs

Part of the authenticated realtime channels rollout (socket auth), one PR per repo:

…annel resolvers

- POST storefront/v1/customers/socket-token mints a customer principal (store key +
  Customer-Token); 404 while socket auth is disabled, 401 without a customer of the
  storefront's company.
- Checkout initialization responses carry socket_token (checkout kind, scp limited to
  checkout.{public_id}) when socket auth is enabled; absent otherwise.
- Register storefront and checkout channel resolvers with core-api's
  SocketChannelRegistry.
- QPay capture publishes {checkout, status, order, error} on checkout.{public_id}
  instead of the raw payment row; a publish failure no longer fails the callback.
- Require fleetbase/core-api ^1.6.69.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant