Skip to content

Remote contract catalogs, first-run platform setup, and SDK session refresh hardening - #120

Open
juicycleff wants to merge 7 commits into
mainfrom
feat/contract-catalog-session-refresh
Open

juicycleff wants to merge 7 commits into
mainfrom
feat/contract-catalog-session-refresh

Conversation

@juicycleff

Copy link
Copy Markdown
Contributor

This lands the commits that were sitting on local main and never reached origin, plus the contract catalog and session refresh work on top of them.

What changes for you

The dashboard extension now fetches a manifest catalog from the upstream authsome, one manifest per installed plugin, and registers every entry as a remote contributor. An older upstream that still returns a single manifest keeps working. Local registration goes through RegisterContractContributor as well, so the embedded contract server and the remote path share one wiring. The notification, organization, and subscription billing plugins gain contract handlers and manifests along the way.

First-run setup grew two optional blocks. auth.setup-status returns safe platform and environment defaults for the form, and auth.setup accepts platform and environment objects that update the bootstrapped platform app and its default environment in place. Names, slugs, the logo URL, the environment type, and metadata size are validated before anything is written. A bad payload leaves the app, the environment, and the user table untouched, and a mutex serializes concurrent setup calls so two first-run requests cannot both pass the empty-deployment check.

On the SDK side, the client-config request keeps the base URL's path prefix. A gateway serving the API at /identity/authsome was being asked at the gateway root, so every page load 404ed. The request now goes through the generated client so the X-Publishable-Key header rides along with the query key. Session refresh is serialized behind a Web Lock, with an in-process queue as the fallback, and always re-reads storage under that lock. A tab that wakes up holding a token another tab already spent adopts the saved replacement. Rotated tokens hit storage before the profile request. A 401 on restore before the advertised expiry triggers a refresh, rejected credentials are cleared so recovery cannot loop back from sign-in, and duplicate initialize calls from React strict mode share one promise.

Forge moves to v1.11.1.

Testing

go build ./..., go vet, and go test on extension and extension/contract pass locally, as does vitest in ui/packages/core and ui/packages/nextjs.

fetchClientConfig and getClientConfig built the request with
new URL("/v1/client-config", baseURL). A leading slash resolves against
the origin only, so an API mounted under a path prefix, the way a
gateway serves it at /identity/authsome, was asked at the gateway root.
Every page load got a 404 and an unhandled AuthClientError, and the
server helper handed the provider a null config.

Both now join the path onto the base, which is what the generated client
does for every other endpoint. Tests pin the prefixed URL on each.
…refresh

The upstream manifest endpoint can now return a catalog of manifests, one
per installed plugin. The extension fetches the catalog, validates and
registers every entry, and still accepts the old single-manifest shape so
an older upstream keeps working. Local registration now goes through
RegisterContractContributor, so the embedded server and the remote path
share one wiring.

auth.setup accepts optional platform and environment blocks. They update
the bootstrapped platform app and its default environment in place, after
validating names, slugs, the logo URL, and metadata size. Setup never
creates or removes environments, and a mutex serializes concurrent calls
so two first-run requests cannot both pass the empty-deployment check.

On the SDK side, the manager serializes refresh behind a Web Lock (with an
in-process queue as the fallback) and always re-reads storage under that
lock. A tab that wakes up holding a token another tab already spent adopts
the saved replacement and skips the exchange. Rotated tokens hit storage
before the profile request, so a failure there cannot lose them. A 401 on
restore before the advertised expiry now triggers a refresh. Rejected
credentials are cleared from storage so recovery cannot loop back from
sign-in. Duplicate initialize calls, as React makes in strict mode, share
one promise.

Client config fetches in core and nextjs now go through the generated
client so the X-Publishable-Key header rides along with the query key.

Tests cover the setup handler end to end (platform, environment, owner
role, session cookie, second-run denial), the catalog fetch and dispatch,
and the session recovery paths.
…setup handler

goimports ordering on three test files, http.NoBody for the catalog
request, named results on validateSetupNameAndSlug, and the platform and
environment update blocks in the setup handler move into two helpers so
the inner err no longer shadows the one validateSetupInput returned.
Behaviour is unchanged.

This branch has not been deployed

No deployments
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