* feat(domains): dual-domain cutover to app.accounted.se The user-facing app moves to app.accounted.se while app.gnubok.se stays alive for machine traffic (MCP connectors, API keys, third-party OAuth callbacks, webhooks, crons), so no third-party callback registration is on the critical path. - next.config: host redirect app.gnubok.se -> NEXT_PUBLIC_APP_URL for page traffic only (/api, /.well-known, /_next excluded). Arms itself only once NEXT_PUBLIC_APP_URL leaves the legacy host, so merging this is inert and the cutover is a pure env flip + redeploy. - skatteverket: redirect_uri pinned via NEXT_PUBLIC_SKV_OAUTH_BASE_URL (Utvecklarportalen registration is slow to change); the OAuth callback now resolves the flow from the state token + stored oauth_user_id via the service client instead of session cookies, which no longer exist on the OAuth host. Legacy same-domain flows fall back to the session. - popup listeners (SkatteverketConnectPanel, AGIPanel) accept postMessage from the pinned OAuth origin; event.source identity check unchanged. - /.well-known discovery docs reflect the allowlisted request host so existing MCP connectors on app.gnubok.se keep a self-consistent issuer/resource after the flip; spoofed hosts fall back to canonical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: log dual-domain cutover decision Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review): recency-bound SKV state lookup, exact localhost match in discovery allowlist - The oauth_state lookup now only considers rows updated in the last 10 minutes: bounds how long a leaked/phished authorize URL stays completable, keeps the row set far below PostgREST's 1000-row cap, and surfaces query errors instead of misreporting them as CSRF. - resolveDiscoveryBaseUrl matches localhost/127.0.0.1 exactly; the prefix check reflected spoofed hosts like localhost.evil.example. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
24 lines
892 B
TypeScript
24 lines
892 B
TypeScript
/**
|
|
* Origins the Skatteverket OAuth popup may post back from.
|
|
*
|
|
* The OAuth callback is served from the host pinned by
|
|
* NEXT_PUBLIC_SKV_OAUTH_BASE_URL: the redirect_uri registered with
|
|
* Skatteverket in Utvecklarportalen, kept on the legacy app.gnubok.se
|
|
* domain after the user-facing app moved to app.accounted.se. The panels
|
|
* that open the popup therefore accept postMessage events from that origin
|
|
* in addition to their own.
|
|
*
|
|
* Origin alone is never sufficient: callers must also verify that
|
|
* event.source is the popup window they themselves opened.
|
|
*/
|
|
export function isAllowedSkvPopupOrigin(eventOrigin: string, windowOrigin: string): boolean {
|
|
if (eventOrigin === windowOrigin) return true
|
|
const base = process.env.NEXT_PUBLIC_SKV_OAUTH_BASE_URL
|
|
if (!base) return false
|
|
try {
|
|
return eventOrigin === new URL(base).origin
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|