fix(auth): resolve auth-link hosts from the brands table, drop NEXT_PUBLIC_WHITELABEL_DOMAINS (#2376)
* fix(auth): resolve auth-link hosts from the brands table, drop NEXT_PUBLIC_WHITELABEL_DOMAINS Password reset, invite, email change and signup links now resolve the request host against brands.domain server-side. The env var was a second copy of that registry compiled into the browser; every new brand needed the row, the env var, the GoTrue allowlist and a redeploy, and two partners shipped with the env var stale, so their reset mails went out canonical-branded to the canonical host. - New POST /api/auth/password-reset: the login page no longer calls GoTrue directly, so the browser carries no domain list. - lib/domains/trusted-app-origin.ts is async and registry-backed; it also trusts this deployment's own VERCEL_URL / VERCEL_BRANCH_URL so previews keep sending links to themselves. - Signup shares the same resolver instead of following the raw host. - Docs and .env.example describe the single registry; GoTrue keeps the redirect allowlist as backstop (hosted: *.accounted.se wildcard). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUNB7qjua8EUaJmZgfFscx * fix(auth): await the async origin resolver in the billing routes merged from main PR #2370 added resolveRequestAppOrigin callers in billing/checkout and billing/portal after this branch made the resolver async. Await them and move their tests from the removed env var to the brands mock; update the login source-assert test to the server-routed reset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUNB7qjua8EUaJmZgfFscx * fix(auth): refuse auth links on a failed brand lookup, keep local dev hosts, correct GoTrue allowlist docs Skeptic and CI findings on #2376, one pass: - A failed brands lookup now throws BrandLookupFailedError (TRANSIENT_ERROR, 503, retryable) instead of falling back to the canonical origin: a canonical link is the wrong-brand mail this PR removes. Password reset and email change answer 503 themselves; withRouteContext routes map the code. - A local canonical (dev) trusts other local hosts and ports on the same scheme, so lane servers on 3001-3003 confirm signups on themselves. - GoTrue matches the full redirect_to including the query and `*` stops at `.` and `/`: docs and decision line now prescribe https://*.accounted.se/auth/callback** and https://*.accounted.se/invite/**. - The Turnstile contract test asserts the server-routed reset forwards the captcha token (it still asserted the removed browser call). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUNB7qjua8EUaJmZgfFscx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
7a30f623ba
commit
d29a5bda14
@@ -1636,6 +1636,7 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-09-06] Bundled SKV ROT/RUT payout books ONE voucher (one 1513 leg per begäran) and the set is suggested at read time with no hint column: one bank row = one verifikat (match-batch precedent) and a uuid[] hint would need six clear paths and go stale; N vouchers + the 1:N reconciliation split was rejected because its half-failure state has no UI exit, and begäran, not the invoice, is the unit under fakturamodellen.
|
||||
[2026-09-06] Utlägg via lön settles claims with an idempotent RPC after the salary verifikat is posted (pre-checked before posting), not with a trigger on salary_runs -> booked: a raise inside that trigger after the entries exist would leave a paid run with posted verifikat and a retry would double-post; the RPC path fails to "booked, claims still open, re-runnable".
|
||||
[2026-09-06] A privately paid supplier invoice is booked through registerExpenseClaim (verifikat + expense_claims row, source_type expense_claim) with the invoice's kontering as custom lines, and a person-paid inbox document goes to the core route with inbox_item_id instead of the extension's convert endpoint: the form's switch, the second entry generator and the convert bypass were three write paths for one fact, so one writer wins over adding a claims insert beside the old generator (the issue's shape) or copying the branch into the convert handler.
|
||||
[2026-09-07] Auth-link hosts resolve against the brands table only; NEXT_PUBLIC_WHITELABEL_DOMAINS removed (supersedes 2026-08-18): the env var was a second copy of brands.domain compiled into the browser, so every brand needed four registrations (row, env var, GoTrue allowlist, redeploy) and two partners shipped with it stale (canonical-branded reset mails). Password reset moved to POST /api/auth/password-reset so the server resolves the host; invite, email change and signup share the same resolver, which also trusts this deployment's own VERCEL_URL/VERCEL_BRANCH_URL so previews keep working. A drift check between the copies was rejected: it would be a fifth thing to maintain. GoTrue's redirect allowlist stays as the backstop; hosted carries the wildcards https://*.accounted.se/auth/callback** and https://*.accounted.se/invite/** there (config, not code; GoTrue matches the full URL with query, and * stops at . and /) so only bring-your-own-domain partners need a manual entry. A failed brands lookup refuses with 503 (BrandLookupFailedError) instead of a canonical fallback: a canonical link is a wrong-brand mail for a white-label user, which is the bug this replaces.
|
||||
[2026-09-07] Draft stamp moved to the page margin (absolute + fixed) instead of the reporter's position:fixed corner badge: the 40pt top margin is the only place that is guaranteed empty on every page, and the stamp must not overlap the header title on the right.
|
||||
[2026-09-07] Hyphenation disabled per Text node in the invoice template, not via a global Font.registerHyphenationCallback: the global hook would also change line breaking in årsredovisning, payslips and every report PDF; that is a separate decision.
|
||||
[2026-09-07] Invoice PDF word wrapping keeps a word whole whenever an ink-width estimate says it fits its column (ordinary Swedish compounds of 17 to 25 characters always do); only a wider token (URL, e-mail, reference) gets break points, after separators and where the column is full, with react-pdf's hyphen at the break (a break after "/" prints "/-"; unavoidable in textkit, accepted over a dropped token). A flat character cap was tried first and rejected: it split ordinary compounds at arbitrary positions. Free-text rows and notes are kept on one page only while a rendered-line estimate (chunks counted, 12 lines max, safe for any font) says they fit; past that they may split, because a non-splittable block taller than a page is clipped silently.
|
||||
|
||||
Reference in New Issue
Block a user