// Routes signed-in users to the surface that matches their role: // - partner staff (User.partnerId set) on '/' → /partner // - non-partner-staff hitting /partner/* → / // - non-admins hitting /admin/* → / // // 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 + admin surfaces share 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 — these redirects plus the // platform-api's per-endpoint role/partnerId checks are the whole defense. Because // of that, /partner/* and /admin/* must fail CLOSED: if we can't positively // confirm the caller's role (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 the wrong role. // // /admin is reachable by tenant admins/owners AND by partner staff (who act as // a customer admin via partner-in-customer mode). We gate on the profile (role / // partnerId), not partner-mode session state, since that isn't resolvable on a // fresh SSR load. // // 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 onAdminSurface = to.path.startsWith('/admin') const { fetchMe, isPartnerStaff, isTenantAdmin } = useMe() const me = await fetchMe() // Couldn't resolve identity. For non-gated routes, defer to the OIDC // middleware's bounce. For partner/admin routes, fail closed — unconfirmed // is not-authorized. if (!me) { return onPartnerSurface || onAdminSurface ? navigateTo('/') : undefined } if (to.path === '/' && isPartnerStaff.value) { return navigateTo('/partner') } if (onPartnerSurface && !isPartnerStaff.value) { return navigateTo('/') } if (onAdminSurface && !isTenantAdmin.value && !isPartnerStaff.value) { return navigateTo('/') } })