Management API: Override document-level security on AllowAnonymous endpoints#22712
Conversation
…dpoints Operations on controllers/actions decorated with [AllowAnonymous] inherit the document-level Bearer security requirement in OpenAPI 3.x unless they explicitly declare an empty security array. Without that override, the generated SDK attaches an Authorization: Bearer header to anonymous endpoints (server/status, server/configuration, install/*, manifest/manifest/public, etc.), which forces a /security/back-office/token refresh during the very first page load. On v18/dev this manifests as a 500 from /server/status during a fresh install: the Authorization header triggers OpenIddict, which resolves UmbracoDbContext from DI, which throws because the connection string is empty in the install state. The transformer now sets operation.Security = [] on AllowAnonymous endpoints so they correctly opt out of the document-level security. The committed OpenApi.json and the regenerated sdk.gen.ts reflect this.
|
Claude finished @iOvergaard's task in 4m 50s —— View job PR ReviewTarget: Fixes a v18/dev-only regression where
Important
Request ChangesThe core fix is correct and the approach is right. The one Important issue (unit tests asserting |
There was a problem hiding this comment.
Pull request overview
Fixes an issue in the Management API OpenAPI generation where document-level security requirements were implicitly inherited by [AllowAnonymous] operations, causing the generated backoffice SDK to treat anonymous endpoints as authenticated and trigger premature token refresh during install/uninitialized startup.
Changes:
- Update
BackOfficeSecurityRequirementsTransformerto explicitly setoperation.Security = []for[AllowAnonymous]endpoints to override document-level security. - Regenerate
OpenApi.jsonso affected anonymous endpoints now include"security": []. - Regenerate the backoffice TypeScript SDK (
sdk.gen.ts) to remove per-operation bearer security declarations for those anonymous endpoints.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Umbraco.Cms.Api.Management/OpenApi/Transformers/BackOfficeSecurityRequirementsTransformer.cs |
Ensures anonymous operations explicitly override document-level security by emitting an empty security array. |
src/Umbraco.Cms.Api.Management/OpenApi.json |
Reflects the override by adding "security": [] to the affected anonymous operations. |
src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts |
Removes bearer security metadata from generated client calls for the affected anonymous endpoints. |
The transformer now sets operation.Security = [] (empty list) on [AllowAnonymous] endpoints to override document-level security, instead of leaving it null. Update the two affected tests to assert the new behaviour and rename them to reflect that the transformer overrides rather than skips security on anonymous operations.
lauraneto
left a comment
There was a problem hiding this comment.
Looks and tests good!
I updated the failing unit tests with the new behavior.
Summary
On v18/dev, opening the backoffice on a freshly checked-out (uninstalled) project crashes the install screen — the very first request to
GET /umbraco/management/api/v1/server/statusreturns a 500 (OpenIddict trying to resolveUmbracoDbContextwith no connection string), and the backoffice firesPOST /umbraco/management/api/v1/security/back-office/tokenbefore the user has even reached the install form.Root cause: PR #21058 (switch to
Microsoft.AspNetCore.OpenApi) caused the OpenAPI document to declaresecurity: [{ Backoffice-User: [] }]at the document level. By the OpenAPI 3.x spec, that requirement is inherited by every operation that does not explicitly override it with its ownsecurityarray — including the empty arraysecurity: [].BackOfficeSecurityRequirementsTransformercorrectly skipped adding a security entry to operations on[AllowAnonymous]actions, but it never inserted the empty-array override, so the generated SDK (sdk.gen.ts) ended up declaringsecurity: [{ scheme: 'bearer', type: 'http' }]ongetServerStatus,getServerConfiguration, the install endpoints, etc. That in turn toldumbHttpClient's auth callback to call#ensureTokenReady()for those calls, which triggered the early/tokenrefresh.This branch is v17 (
main) is not affected becausemain's checked-insdk.gen.tspredates theMicrosoft.AspNetCore.OpenApiswitch and contains no document-level security default.Changes
BackOfficeSecurityRequirementsTransformer: when an operation's action method (or its declaring type) is[AllowAnonymous], setoperation.Security = []so the operation overrides the document-levelBackoffice-Userrequirement.OpenApi.json: 10 anonymous operations now carry"security": [](/server/status,/server/configuration,/install/settings,/install/setup,/install/validate-database,/manifest/manifest/public,/previewDELETE,/security/forgot-password/verify,/user/invite/create-password,/user/invite/verify).sdk.gen.ts: matchingsecurity: [...]arrays removed from those 10 operations.Test plan
Reproduced and verified in a fresh worktree (
v18/dev) with empty connection string:GET /server/status→ 500,POST /security/back-office/tokenfires before the install screen renders, OpenIddict stack trace in console.GET /server/status(200),GET /server/configuration(200),GET /manifest/manifest/public(200),GET /install/settings(200). NoAuthorizationheader on those calls. No/tokenrequest./umbraco/section/content).Reported by @nielslyngsoe on Slack.