// Routes signed-in users to the surface that matches their role: // - partner staff (User.partnerId set) on '/' → /partner // - non-partner-staff hitting /partner/* → / // // Runs after the OIDC global middleware (00.auth.global from nuxt-oidc-auth) // so we know the user is authenticated by the time we get here. /me is // fetched lazily via useMe() and cached in useState — first nav after sign-in // pays one round-trip, subsequent navs read from cache. // // The partner surface shares the dezky-portal OAuth client with ordinary // tenant users (a tenant admin authenticates here legitimately), so there is // no IdP-level gate the way the operator app has — this redirect plus the // platform-api's per-endpoint partnerId checks are the whole defense. Because // of that, /partner/* must fail CLOSED: if we can't positively confirm the // caller is partner staff (e.g. /api/me errored transiently, so `me` is null), // we keep them out rather than letting the page shell render. Data is always // backend-guarded, but the shell shouldn't show to a non-partner. // // Auth pages (/auth/*, /signed-out) are skipped because they're public. export default defineNuxtRouteMiddleware(async (to) => { if (to.path.startsWith('/auth/') || to.path === '/signed-out') return const onPartnerSurface = to.path.startsWith('/partner') const { fetchMe, isPartnerStaff } = useMe() const me = await fetchMe() // Couldn't resolve identity. For non-partner routes, defer to the OIDC // middleware's bounce. For partner routes, fail closed — unconfirmed is // not-partner. if (!me) { return onPartnerSurface ? navigateTo('/') : undefined } if (to.path === '/' && isPartnerStaff.value) { return navigateTo('/partner') } if (onPartnerSurface && !isPartnerStaff.value) { return navigateTo('/') } })