diff --git a/.changeset/17210-oauth-register-name-trap-prose.md b/.changeset/17210-oauth-register-name-trap-prose.md new file mode 100644 index 0000000000..986ddc0ea5 --- /dev/null +++ b/.changeset/17210-oauth-register-name-trap-prose.md @@ -0,0 +1,28 @@ +--- +'@objectstack/client': patch +--- + +`oauth.applications.register`'s docblock says where a plain `name` IS honoured, and that it is not this route + +A caller who wants to name an OAuth client reaches for `name`. On the route this +method posts — the provider's `/oauth2/create-client` — that member is not in +the body schema and is stripped: driven on a real socket, the call answered +**201** and the value was absent from the response, from `applications.get`, +from `applications.list`, and `null` in the `sys_oauth_application` row's `name` +column. Nothing in the answer says so. + +The spelling is not wrong everywhere, which is what made it worth writing down: +`POST /api/v1/auth/sys-oauth-application/register` — the session-required +ObjectStack mount behind the Console's *Setup → OAuth Applications* form — +answered **200** to the same body, mapped `name` onto `client_name`, and set +that column. That mount is `disposition: 'server-only'` in the auth route ledger +and objectstack#17210 ruled it stays that way, so no SDK method builds its URL. + +The docblock now states both halves where the caller reads them: post +`client_name` to name a client from here, and `redirect_uris` must arrive +pre-split — the newline-separated-textarea split is the Console wrapper's, not +this route's. + +Docblock only. No method is added, no request or response type changes, and the +ledger row is untouched — but the text ships inside `dist/*.d.ts` as editor +hover, so it is a `patch` rather than a no-publish change. diff --git a/content/docs/protocol/objectui/actions.mdx b/content/docs/protocol/objectui/actions.mdx index 0e44483483..77c2a7e242 100644 --- a/content/docs/protocol/objectui/actions.mdx +++ b/content/docs/protocol/objectui/actions.mdx @@ -435,6 +435,12 @@ resultDialog: Each `fields[].path` is a dot path into `result.data`. The per-field `format` (`qrcode`, `code-list`, `secret`, `text`, `json`) controls rendering. +#### ⚠️ That `target` is not interchangeable with the SDK's registration door + +`POST /api/v1/auth/sys-oauth-application/register` is an **ObjectStack mount written for this form**, and it is what keeps the YAML above this short. It accepts the application's display name as a plain `name` body key — the spelling an author writes as a param — and it splits a newline-separated redirect-URL **textarea** into the `redirect_uris` array the underlying provider schema requires. Both are form-shaped conveniences the mount performs on the action's behalf; the route ledger carries it as `disposition: server-only` (`packages/plugins/plugin-auth/src/auth-route-ledger.ts`) for exactly that reason, and [#17210](https://github.com/objectstack-ai/objectstack/issues/17210) ruled that it stays that way — no SDK method builds this URL. + +So do **not** re-point this `target` at the provider's own registration route, `/api/v1/auth/oauth2/create-client` (what `@objectstack/client`'s `oauth.applications.register` posts). Driven on a real socket, the two doors answer the same body differently: the mount above returned **200**, mapped `name` onto `client_name`, and set the `sys_oauth_application` row's `name`; the provider route returned **201** with the value **stripped** — absent from the response, from `oauth.applications.get`, from `oauth.applications.list`, and `null` in that column. An action retargeted there needs its name param renamed to `client_name`, and its redirect URLs split into an array before the post — the textarea param would arrive as one unsplit string. + ## Action Parameters `params` declares inputs collected from the user before execution. Each entry is an `ActionParam` with two modes: diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index fa47e56119..4a2030cf86 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -4038,6 +4038,33 @@ export class ObjectStackClient { * router skips SERVER_ONLY endpoints, so over HTTP it answers 404 with a * zero-byte body. * + * ## ⚠️ A plain `name` IS honoured somewhere — not here (#17210) + * + * There is exactly one door that reads a body member spelled `name`, and + * this method does not build it: + * `POST /api/v1/auth/sys-oauth-application/register`, the ObjectStack + * mount behind the Console's *Setup → OAuth Applications* create form. + * The same #15447 round drove both doors on one real socket: that mount + * answered **200** to a body spelled `name`, mapped it onto + * `client_name`, and the `sys_oauth_application` row's `name` column was + * set; this method's route, `/oauth2/create-client`, answered **201** + * with the value **stripped** — absent from the response, absent from + * `applications.get`, absent from `applications.list`, and `null` in + * that same column. + * + * ⛔ That mount is **not** an SDK door, and it is not withheld by + * oversight: it is `disposition: 'server-only'` in the auth route ledger + * (`packages/plugins/plugin-auth/src/auth-route-ledger.ts`), a + * session-required self-service wrapper written to serve the Console's + * form — and #17210 ruled that it **stays** `server-only`, so no SDK + * method builds its URL. To name a client from here, post `client_name`. + * + * The wrapper's other Console-shaped convenience is the same asymmetry's + * second half: it splits that form's newline-separated redirect-URL + * **textarea** into the array the vendor schema requires. ⛔ This route + * performs no such split — `redirect_uris` must arrive **pre-split**, + * one entry per URL, which is what an SDK caller holds anyway. + * * Pinned by `oauth-applications-register-request-members.test.ts`. */ register: async (req: {