Skip to content

Connect with Fleetbase (OAuth) and automatic webhook registration - #2

Merged
roncodes merged 1 commit into
mainfrom
feat/connect-with-fleetbase
Oct 28, 2026
Merged

roncodes merged 1 commit into
mainfrom
feat/connect-with-fleetbase

Conversation

@roncodes

@roncodes roncodes commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

Setup no longer asks the merchant to create and paste an API key, or to copy a webhook URL into the Fleetbase console.

Connect with Fleetbase

  • The wizard's connect step leads with Connect with Fleetbase. "Paste an API key instead" keeps the old form.
  • The store registers itself as a public PKCE OAuth client (POST {host}/oauth/register, software_id=fleetbase-woocommerce, redirect {admin_url}admin-post.php?action=fleetbase_wc_oauth). Registration is idempotent and runs on every start.
  • The merchant approves the store for an organization on Fleetbase's consent screen, which shows "Fleetbase for WooCommerce (shop domain)". Scopes: organization:read api-key:create, prompt=consent.
  • The callback exchanges the code, calls POST {host}/oauth/v1/api-key, checks the key with organizations/current, and saves it through ConnectionStore::save(), exactly as a pasted key is saved. The plugin keeps running on an API key; the OAuth tokens are dropped and never revoked, because revoking the grant expires the key. Removing the store under Connected apps in Fleetbase disconnects it.
  • Server-side calls always use the connection host; only the authorization endpoint comes from discovery, because the browser opens it.
  • Failures come back as ?fleetbase_oauth=<reason> and the wizard explains them in a sentence.

Automatic status updates

  • After any connect, Sync\WebhookRegistrar registers the webhook URL with /v1/webhook-endpoints (core-api 1.6.74) for the order events the receiver acts on, adopting an existing endpoint for the same URL, and stores the endpoint's signing secret encrypted.
  • While the secret is stored, the receiver requires a valid X-Fleetbase-Signature (5 minute tolerance) as well as the URL token.
  • Regenerating the URL updates the endpoint; disconnecting or switching connections deletes it first (new fleetbase_wc_before_disconnect action).
  • When Fleetbase refuses the URL (localhost and private networks, or a server without the endpoint API), the webhook card shows the reason and the manual steps, and the poller keeps orders in step. A "Set up automatically" button retries.

Also fixed

  • scripts/seed-store.sh generates the sandbox admin password into the gitignored .sandbox.env, and the end-to-end setup reads it. The password was still hardcoded on main, and .sandbox.env was not ignored.

Depends on

fleetbase/core-api#327 (dynamic client registration and the api-key:create scope), into release/v1.6.74.

Test plan

  • 22 new unit tests: PKCE, the OAuth client (discovery, registration, authorize URL, public-client token exchange, missing scope, unknown client, api-key errors), webhook signatures (valid, wrong, tampered, stale, missing), webhook registration (create, keep, adopt and rotate, URL change, event-name fallback, refused URL, removal). 87 tests pass.
  • composer lint, composer analyse (PHP 8.3 and the 8.1 config), pnpm lint:js, pnpm build.
  • Local stack, up to consent: the setup page's Connect URL registers the sandbox store at /oauth/register (same client as a direct registration, so idempotent) and redirects to /oauth/authorize with both scopes, S256 and prompt=consent; Fleetbase hands it to the console consent screen.
  • Callback paths: declined consent returns fleetbase_oauth=access_denied; a replayed or forged state returns state_mismatch; a signed-out callback goes to the WordPress login; the existing connection is untouched by all of them.
  • Webhook fallback: POST fleetbase/v1/webhook/register against the local API stores "The url must be a public HTTP or HTTPS URL." and keeps the manual card.
  • Approving consent and landing connected. Needs a person signed in to the local console as an administrator of Woo Test Store.
  • Existing Playwright suite: 3 passed, 6 failed, 3 skipped, none in code this PR touches. Block and classic checkout show no rates because the local API's quote endpoint fails ("A GeometryEngine must be set": Fleet-Ops registers GEOS only when the extension is loaded, and it isn't in the FrankenPHP workers). The Deliveries, Drivers and Vehicles pages fail axe colour-contrast (WooCommerce status badges) and unlabelled row selects, on pages this PR doesn't change.

- Connect with Fleetbase: the setup wizard's connect step signs the merchant in
  to Fleetbase instead of asking for a pasted key. The store registers itself as
  a public PKCE client (POST /oauth/register, software_id
  fleetbase-woocommerce), the merchant approves it for an organization, and
  Fleetbase creates the store's API key (POST /oauth/v1/api-key), which is then
  saved exactly like a pasted key. Tokens are dropped, never revoked. Needs
  core-api 1.6.74 with fleetbase/core-api#327. Pasting a key still works.
- Status updates are registered automatically through /v1/webhook-endpoints
  after any connect, kept in step when the URL is regenerated, and removed on
  disconnect. Deliveries must carry a valid X-Fleetbase-Signature while the
  plugin holds the endpoint's secret. When Fleetbase refuses the URL (localhost,
  private networks, older servers) the card shows why and the manual steps.
- The sandbox admin password is generated into the gitignored .sandbox.env by
  scripts/seed-store.sh and read by the end-to-end setup; it is no longer
  hardcoded.
- Tests for PKCE, the OAuth client, webhook signatures and registration.
@github-actions

Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@roncodes
roncodes merged commit 2a30ce5 into main Oct 28, 2026
6 checks passed
@roncodes
roncodes deleted the feat/connect-with-fleetbase branch October 28, 2026 02:04
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