* feat(white-label): WL-14 cockpit landing for BankID and OAuth/magic-link logins Byra staff logging in via BankID or the Google/magic-link callback on their brand domain landed on /select-company resp. / instead of the cockpit, because those two paths bypassed the WL-14 landing rule. - Extract the rule into resolveLandingDestination (lib/company/landing-server.ts) so server code can call it without an HTTP round-trip; /api/clients/landing becomes a thin wrapper. - Auth callback: with no explicit destination, AAL1 sessions resolve the landing from the request host, degrading to / on any failure (MFA-enrolled users already get the rule via /mfa/verify). - BankID login: byra staff on their brand host get /clients; everyone else keeps the deliberate /select-company picker byte-identically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(white-label): address PR 1972 review findings - /api/clients/landing: requireAuth() directly instead of withRouteContext, which 4xxed byra staff without a company of their own (COMPANY_CONTEXT_MISSING) and silently sent the cockpit's primary persona to /select-company. MFA enforcement unchanged. - landing-server: log the byra membership query error before degrading to '/' so a persistent failure is distinguishable from no membership. - Deduplicate the clientWithTeamMembership test mock to file scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(white-label): paginate the byra membership query fetchAllRows per repo convention: PostgREST silently caps unpaginated selects at 1000 rows, which could hide a qualifying owner/admin membership. Errors still degrade to '/' with a log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
31 lines
1.3 KiB
TypeScript
31 lines
1.3 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { requireAuth } from '@/lib/auth/require-auth'
|
|
import { resolveLandingDestination } from '@/lib/company/landing-server'
|
|
|
|
/**
|
|
* GET /api/clients/landing
|
|
*
|
|
* Post-login landing decision (WL-14): thin HTTP wrapper around
|
|
* resolveLandingDestination (lib/company/landing-server.ts), for the client
|
|
* auth surfaces (login and MFA-verify pages) that cannot call it in-process.
|
|
* The helper carries the whole rule, including the owner/admin role gate
|
|
* (2026-08-27). Called when no explicit destination was requested; any
|
|
* failure degrades to '/' at the caller.
|
|
*
|
|
* Uses requireAuth() directly (the sanctioned withRouteContext opt-out, MFA
|
|
* still enforced) because the decision needs no active company. Byrå staff
|
|
* without a company of their own are the cockpit's primary persona and must
|
|
* still land on /clients; withRouteContext would 4xx them with
|
|
* COMPANY_CONTEXT_MISSING.
|
|
*/
|
|
export async function GET(request: Request) {
|
|
const auth = await requireAuth()
|
|
if (auth.error) return auth.error
|
|
const { user, supabase } = auth
|
|
|
|
const host =
|
|
request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? ''
|
|
const destination = await resolveLandingDestination(supabase, user.id, host)
|
|
return NextResponse.json({ data: { destination } })
|
|
}
|