| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
AddTenantedDataProtection deliberately renders ASP.NET Core's root DataProtection key manager inert — the per-realm providers own the real key rings. Its DataProtectionHostedService stayed registered though, and that service resolves the root key ring on StartAsync to surface problems early. Against an inert manager the read can never succeed, so every cold start logged two ERROR lines describing a state we chose on purpose. For an IdP that is expensive noise: an operator has to be able to trust that an error at boot means something is genuinely broken. Remove that one hosted-service registration. It has to happen after every other registration: OpenIddict's builder calls AddDataProtection(), and the TryAddEnumerable puts the check straight back precisely because we removed it. Dropping it only inside AddTenantedDataProtection therefore leaves the boot log unchanged. Program.cs calls the idempotent extension again right before Build(). Only the root registration is touched. The per-tenant containers keep normal key generation against MartenXmlRepository, so a genuine per-realm key-ring failure still surfaces. Verified on a cold start: zero ERR/FTL lines where there were two, and a session cookie issued before the change still decrypts afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vite bound ::1 only, so a proxy pointed at 127.0.0.1:4300 got connection refused, and it rejected Host headers it did not recognise. Both stand in the way of reaching the dev server through a local HTTPS proxy — which is what WebAuthn, Secure cookies and OIDC redirect_uris want, rather than a bare port. Browsers using localhost:4300 are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… boundary Page Builder 3.0 stamps saved documents as schemaVersion 6, and the one document change it carries is repeat.props.source -> props.contextPath. Our server-side publish validator pinned the version at 5 and read the repeat source only under its old name, so every document the new builder produced would have been rejected by our own API — twice over, and with a message pointing at neither cause. Raise the accepted version and read contextPath first, falling back to source. That mirrors the package's own migration, which skips when the new key is already present. MinimumSchemaVersion stays at 4, so documents stored before the rename keep validating, and the allowlist still bites through the new spelling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ontract
3.0 removes `createAuthPageConfig()` and `createAuthPageDocument()`: the
package ships the runtime and nothing auth-specific. That is the right
boundary — the element, action and context allowlists decide what a
tenant-authored document may reach, so they belong to the IdP, not to a
generic authoring library.
The config is now ours (authPageConfig.ts), built from the shape the
package's IDP_INTEGRATION.md describes. The starting documents are ours
too, and rather than re-author them by hand — which would have silently
changed what a realm gets on "create page" — scripts/freeze-auth-documents
captured the existing derivation against the last pre-3.0 build and froze
it as JSON. What a realm gets is what it got before.
Migrated with them, since neither is an automatic ingest migration:
- 14 `visibleWhen: { source: 'state' }` conditions now read
`runtime.viewState` from the runtime context, which every caller already
passed. Page State bindings keep the name; there were none.
- 2 repeaters name their array through `contextPath`, and the documents are
stamped schemaVersion 6.
- The page root no longer carries `minHeight`, in the style or in Root Page
Code. 3.0 drops size on the root because the page is exactly its host
container, and every new page opened with a warning about a line that did
nothing. The container supplies the height already.
Renames applied: `config.fields` → `dataContract`, `config.elements` →
`elementTypes`, `PageVisualFont.source` → `src`. Note that
`normalizePageSchema`'s option key stays `elements` — only the PageConfig
property moved. Removed props: the renderer's `viewState` and the builder's
`previewState`, both redundant with the context. `availableStates` becomes
`runtime.viewState` with `allowedValues`, which is what restores the
dropdown in the condition editor; `previewFixtures` becomes host-owned
`previewViewports`; `stylePresets` is gone with nothing to replace it.
All six @cocoar/vue-* packages pinned to 3.0.0 as the guide asks, so an
authoring package cannot pair with an older renderer.
Verified end to end against the dev instance: create login page → save
(accepted by the schemaVersion 6 publish boundary) → activate in realm
settings → the runtime renders the document through CoarPageRenderer with
the SES page-script worker running. No console errors, no backend errors,
type-check and production build clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reference is the real page at https://app.amzettel.at, and the question it answers is whether a realm can rebuild its own login without us shipping anything tenant-specific. It can: the split screen, the brand panel and the passwordless form are ordinary authored content, going in through the same API the admin UI uses. The brand panel is a composition — a visual-markup node with the wordmark, tagline and the shopping list that ticks itself off. Login and logout can pin the same immutable version, so updating it is one publish rather than an edit per page. The form is the flow the reference shows: an email address, a mailed code, a passkey alternative. The code step is not a second page but the same document reacting to runtime.viewState. Colour, radii and fonts are deliberately NOT in the document. They come from the application theme, which is why amZettel is modelled as what it is — an application of the realm, on its own subdomain, inheriting the realm's active login variant. The same document renders in another application's colours without touching a node. Two things this surfaced: - The visual-markup vocabulary was six variables, too thin for a real brand panel. It is now a documented set (surface, ink shades, lines, brand, radius, shadow, easing, fonts) fed from the application theme. The names are generic on purpose: the predecessor was a stylePreset with one tenant's background baked into our stylesheet, which only that tenant could use — and which 3.0 removed anyway. - LoginView decided whether the page owns the language switcher by looking for the node id `login-language-switcher`, so a page that named its switcher anything else got a second one drawn by the host. It now matches on the `auth:toggle-language` action, which is the published contract; node ids belong to the author. Verified against the reference at 1280 and on the phone viewport, where the panel hides itself and the form takes the screen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wordmark rendered far smaller than the reference. Inside the sealed visual-markup iframe, `vw` resolves against the iframe — this pane — not the viewport, so the original design's `clamp(2.6rem, 4.5vw, 3.4rem)` measured 4.5% of 563px and collapsed onto its clamp minimum. The panel is 44% wide, which makes the equivalent ~10vw. Worth remembering for any visual-markup content: viewport units inside the sealed frame are pane units. The seed script now publishes a new composition version when the panel changed, rather than silently reusing the old one — published versions are immutable, and a page stays pinned to what it materialized. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| Back | FazBrowse Home | New Git URL |
Motivation
Ships the end-to-end Page Builder customization core for authentication pages, including compositions, page variants, application-scoped themes, email branding, preview/runtime alignment, and the amZettel login composition.
Also fixes the refresh-token response-loss incident tracked in Atlas as bug-token-500-replay-kill-logout: late metric failures can no longer turn a successful token mint into a 500, immediate retries receive a 30-second reuse leeway, and genuine replay remains isolated to the affected device authorization.
Surface touched
Tests
Local verification for the refresh-token fix:
Docs
Breaking changes
Notes
The OAuth hardening keeps the successful token response independent from observability lookups, configures a 30-second OpenIddict refresh-token reuse leeway, and aligns the custom reuse audit handler with that same window. Reuse after the window still triggers invalid_grant and revocation, while another device authorization remains usable.