nuxt-supabase-team-auth
Version:
Drop-in Nuxt 3 module for team-based authentication with Supabase
81 lines (80 loc) • 3.23 kB
JavaScript
import { useTeamAuth } from "../composables/useTeamAuth.js";
import { navigateTo, defineNuxtRouteMiddleware, useRuntimeConfig } from "#imports";
export default defineNuxtRouteMiddleware(async (to) => {
if (import.meta.server) return;
const { currentUser, currentTeam, currentRole, isLoading, isImpersonating } = useTeamAuth();
if (isLoading.value) {
let attempts = 0;
const maxAttempts = 20;
while (isLoading.value && attempts < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, 100));
attempts++;
if (currentUser.value !== void 0) {
break;
}
}
if (isLoading.value && attempts >= maxAttempts) {
console.warn("[Team Auth] Auth loading timeout in middleware, proceeding anyway");
}
}
const config = useRuntimeConfig();
const teamAuthConfig = config.public.teamAuth || {};
const defaultProtection = teamAuthConfig.defaultProtection || "public";
const configuredProtectedRoutes = teamAuthConfig.protectedRoutes || ["/dashboard"];
const configuredPublicRoutes = teamAuthConfig.publicRoutes || [];
const authRoutes = [
"/login",
"/signin",
"/signup",
"/auth",
"/confirm",
"/reset-password",
"/forgot-password",
"/accept-invite"
];
let protectedRoutes;
let publicRoutes;
if (defaultProtection === "public") {
protectedRoutes = configuredProtectedRoutes;
publicRoutes = ["/", ...authRoutes, ...configuredPublicRoutes];
} else {
protectedRoutes = ["/dashboard", "/team", "/teams", "/admin", "/profile", "/settings"];
publicRoutes = ["/", ...authRoutes, ...configuredPublicRoutes];
}
const currentPath = to.path;
const isPublicRoute = publicRoutes.some(
(route) => currentPath === route || currentPath.startsWith(route + "/")
);
if (isPublicRoute) {
return;
}
const isProtectedRoute = protectedRoutes.some(
(route) => currentPath.startsWith(route)
);
if (isProtectedRoute && !currentUser.value) {
const redirectUrl = `${currentPath}${to.search ? `?${new URLSearchParams(to.query).toString()}` : ""}`;
const loginPage = teamAuthConfig.loginPage || "/signin";
return navigateTo(`${loginPage}?redirect=${encodeURIComponent(redirectUrl)}`);
}
if (isImpersonating.value) {
if (currentPath.startsWith("/admin/") && !currentPath.includes("/impersonate/stop")) {
return navigateTo("/dashboard?error=admin_blocked_during_impersonation");
}
}
if (currentPath.includes("/admin/impersonate") || currentPath.includes("/impersonate")) {
if (currentRole.value !== "super_admin") {
return navigateTo("/dashboard?error=insufficient_permissions");
}
}
if (currentPath.startsWith("/teams/") && currentPath !== "/teams") {
const teamIdFromRoute = currentPath.split("/teams/")[1]?.split("/")[0];
if (teamIdFromRoute && currentTeam.value?.id !== teamIdFromRoute) {
return navigateTo("/teams?error=unauthorized_team_access");
}
}
const teamRequiredRoutes = ["/team/", "/dashboard"];
const requiresTeam = teamRequiredRoutes.some((route) => currentPath.startsWith(route));
if (requiresTeam && currentUser.value && !currentTeam.value) {
return navigateTo("/teams?message=select_team_first");
}
});