Repository navigation
feat(oauth-server): dynamic client registration and the api-key:create scope - #327
Merged
Merged
Conversation
…e scope Software installed on many sites, such as the Fleetbase for WooCommerce plugin, cannot share one registered redirect URI, so each install now registers its own public PKCE client at POST /oauth/register (RFC 7591). - Only software listed under oauth-server.registration.software may register, with the scopes listed there; the request supplies the software id and exactly one redirect URI. Clients are never official and are named after the redirect host, so the consent screen and Connected apps show the site. Registering again returns the same client; disabled clients are refused; clients that never get a grant are pruned after a day. Advertised as registration_endpoint in discovery; OAUTH_SERVER_REGISTRATION_ENABLED turns it off. - api-key:create scope and POST /oauth/v1/api-key: creates or returns the live API key an app minted in the token's organization, tracked by the new api_credentials.oauth_client_id rather than by name. - Revoking an app's last grant in an organization (Connected apps, admin revoke, disabling the app) expires the keys it minted there.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## release/v1.6.74 #327 +/- ##
====================================================
Coverage 100.00% 100.00%
- Complexity 9562 9600 +38
====================================================
Files 518 519 +1
Lines 29685 29841 +156
====================================================
+ Hits 29685 29841 +156
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…i-key
For an app that serves many installations from one client, such as the Shopify
app (one official client, one key per shop):
- POST /oauth/v1/api-key takes an optional installation (string, at most 190
characters). With it, the key is found or minted by (company, client,
installation) and named "{app} ({installation})", stored in the new
api_credentials.oauth_installation. Without it nothing changes.
- DELETE /oauth/v1/api-key, same scope and optional installation, expires only
the calling app's key for that installation in the token's company (the
app-wide key when installation is omitted). 204, idempotent; the grant and
other installations' keys are untouched.
- Revoking the app's last grant in the organization still expires all of its
keys, installation keys included.
5 of 7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets software installed on many sites connect with "Continue with Fleetbase". The first user is the Fleetbase for WooCommerce plugin: every store runs on its own domain, and the OAuth server only accepts exact, pre-registered redirect URIs, so one shared client cannot work.
Dynamic client registration (RFC 7591)
POST /oauth/register(throttle 10/min), advertised asregistration_endpointin discovery.oauth-server.registration.softwaremay register, and only with the scopes listed there. Today that isfleetbase-woocommercewithorganization:readandapi-key:create. The request supplies the software id and exactly one redirect URI; nothing else is taken from it.oauth_clients.registration_key). A client an admin disabled is refused.oauth-server:prunedeletes registered clients that never got a grant afterunused_client_hours(24).OAUTH_SERVER_REGISTRATION_ENABLED=falseturns it off and removes it from discovery.api-key:createscopePOST /oauth/v1/api-keycreates a live API key named after the app in the token's organization, or returns the one that app created before. Keys are tracked by the newapi_credentials.oauth_client_id, never by name, so a hand-made key with the same name is never handed out. The console sandbox header cannot turn it into a test key.Installations of one shared app (for the Shopify app: one official client, one key per shop)
POST /oauth/v1/api-keytakes an optionalinstallation(string, at most 190 characters, no control characters; blank means none). With it, the key is found or minted by (company, client, installation) and named "{app} ({installation})". Without it, behaviour is unchanged.DELETE /oauth/v1/api-key, same middleware and optionalinstallation, expires only the calling app's key for that installation in the token's company (the app-wide key when omitted). 204 and idempotent; the grant and other installations' keys are untouched.OAuthGrant::revoke()or the bulk disable path, and both expire the app's keys once no grant is left, installation keys included.Migration
2026_10_27_000001_add_oauth_registration_columns:oauth_clients.software_id,oauth_clients.registration_key(unique),api_credentials.oauth_client_id,api_credentials.oauth_installation.No console change is needed: the consent screen already shows the app name and redirect host.
Test plan
OAuthServerRegistrationTest(28 tests, including installations A and B getting different keys, repeat mints returning the same key, DELETE A leaving B, DELETE without installation leaving installation keys, grant revoke expiring all of them, and invalid installations refused): registration, idempotency, loopback development clients, refusals, disabled registration and clients, discovery, PKCE-only sign-in limited to the registered redirect, key create and reuse, ignoring same-name keys, scope required, expiry on the last grant and on disabling the app, pruning. The real migration runs in the test setup.php-cs-fixerand the test date-drift check are clean.registration_endpointandapi-key:create; registering the sandbox store twice returns the same client; an unknown software id getsinvalid_client_metadata;/oauth/authorizehands the registered redirect to the console consent screen and refuses a foreign redirect on Fleetbase.