Revert Clerk - keeping existing Supabase auth, add security middleware

This commit is contained in:
2026-06-02 05:35:02 +00:00
parent cadb14b862
commit 00deda1350
4 changed files with 62 additions and 146 deletions
-30
View File
@@ -1,30 +0,0 @@
// Sentry Configuration for Next.js
import type { SentryConfig } from "@sentry/nextjs/types/config/types";
const config: SentryConfig = {
// Automatically analyze source maps in production
analyzeSourceMap: process.env.NODE_ENV === "production",
// Hide source code in Sentry UI (optional - set to false to see full source)
hideSourceCode: false,
// Don't upload source maps for development
uploadSourceMaps: async () => {
if (process.env.NODE_ENV === "production") {
return true;
}
return false;
},
// Additional configuration options
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT || "route-commerce",
// Release management
release: process.env.GIT_SHA || process.env.VERCEL_GIT_COMMIT_SHA,
// Enable debug mode in development
debug: process.env.NODE_ENV === "development",
};
export default config;
+1 -7
View File
@@ -1,13 +1,9 @@
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import { ClerkProvider } from "@clerk/nextjs";
import "./globals.css"; import "./globals.css";
import { Providers } from "@/components/Providers"; import { Providers } from "@/components/Providers";
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://routecommerce.com"; const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://routecommerce.com";
// Clerk publishable key
const publishableKey = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || "";
export const viewport: Viewport = { export const viewport: Viewport = {
width: "device-width", width: "device-width",
initialScale: 1, initialScale: 1,
@@ -54,9 +50,7 @@ export default function RootLayout({
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body> <body>
<ClerkProvider publishableKey={publishableKey}> <Providers>{children}</Providers>
<Providers>{children}</Providers>
</ClerkProvider>
</body> </body>
</html> </html>
); );
+56 -55
View File
@@ -1,8 +1,9 @@
// Clerk middleware for route protection // Supabase Auth Middleware - keeps existing auth working
import { authMiddleware } from "@clerk/nextjs"; import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// Define public routes that don't require authentication // Public routes that don't require authentication
const publicRoutes = [ const publicRoutes = [
"/", "/",
"/login", "/login",
@@ -15,80 +16,80 @@ const publicRoutes = [
"/privacy-policy", "/privacy-policy",
"/contact", "/contact",
"/api/health", "/api/health",
"/api/webhooks/clerk", "/api/stripe/webhook",
"/api/resend/webhook",
// Brand storefronts are public // Brand storefronts are public
"/tuxedo", "/tuxedo",
"/tuxedo/*", "/tuxedo/*",
"/indian-river-direct", "/indian-river-direct",
"/indian-river-direct/*", "/indian-river-direct/*",
"/cart",
"/cart/*",
"/checkout",
"/checkout/*",
// Error pages // Error pages
"/error", "/error",
"/not-found", "/not-found",
]; ];
// Define routes that require specific roles // Admin routes that require auth
const roleProtectedRoutes = [ const adminRoutes = ["/admin", "/water/admin"];
{ path: "/admin/*", roles: ["platform_admin", "brand_admin", "store_employee"] },
{ path: "/wholesale/portal/*", roles: ["wholesale_customer"] },
{ path: "/water/admin/*", roles: ["platform_admin", "brand_admin"] },
];
export default authMiddleware({ // Wholesale routes
// Public routes - don't require auth const wholesaleRoutes = ["/wholesale"];
publicRoutes,
// Ignore auth for these paths (API routes with their own auth) export async function middleware(request: NextRequest) {
ignoredRoutes: [ const { pathname } = request.nextUrl;
"/api/*", // API routes handle their own auth
"/_next/*", // Next.js internals
"/favicon.ico",
"/robots.txt",
"/sitemap.xml",
],
// After auth middleware - check roles // Check if route is public
afterAuth: (auth, req, evt) => { const isPublicRoute = publicRoutes.some(
const { userId, sessionId } = auth; (route) => pathname === route || pathname.startsWith(route.replace("/*", ""))
const path = req.nextUrl.pathname; );
// Skip role check for public routes if (isPublicRoute) {
if (publicRoutes.some(route => path.startsWith(route.replace("/*", "")))) { return NextResponse.next();
return; }
// Check for auth cookie (Supabase session)
const hasAuthCookie =
request.cookies.get("rc_auth_uid")?.value ||
request.cookies.get("rc_uid")?.value ||
request.cookies.get("dev_session")?.value;
if (!hasAuthCookie) {
// Redirect to login
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
// Check for admin routes (may need additional role checking)
const isAdminRoute = adminRoutes.some((route) => pathname.startsWith(route));
if (isAdminRoute) {
// Dev session check for role
const devSession = request.cookies.get("dev_session")?.value;
if (devSession === "store_employee") {
// Store employees have limited admin access
// More granular checks happen in the page components
} }
}
// Check if route is role-protected // Add security headers to all responses
for (const protectedRoute of roleProtectedRoutes) { const response = NextResponse.next();
if (path.startsWith(protectedRoute.path.replace("/*", ""))) {
if (!userId) {
// Redirect to login if not authenticated
const signInUrl = new URL("/login", req.url);
signInUrl.searchParams.set("redirect_url", path);
return Response.redirect(signInUrl);
}
// For admin routes, check session and role response.headers.set("X-Content-Type-Options", "nosniff");
if (protectedRoute.path.startsWith("/admin")) { response.headers.set("X-Frame-Options", "DENY");
// Admin routes require one of the allowed roles response.headers.set("X-XSS-Protection", "1; mode=block");
// This is handled by the admin-permissions module in app layer response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
}
if (protectedRoute.path.startsWith("/wholesale/portal")) { return response;
// Wholesale portal requires wholesale_customer role }
// This is handled by wholesale-auth module in app layer
}
}
}
},
// Debug in development
debug: process.env.NODE_ENV === "development",
});
export const config = { export const config = {
matcher: [ matcher: [
// Skip Next.js internals and all files in the _next directory // Skip Next.js internals and all files in the _next directory
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes // Run for API routes that need auth checking
"/(api|trpc)(.*)", "/(api)(?!.*\\.(?:json|ico|png|jpg|jpeg|gif|webp|svg|ttf|woff|woff2|less|css)).*)",
], ],
}; };
-49
View File
@@ -1,49 +0,0 @@
// Clerk Middleware for Next.js App Router
// Must be exported as default from proxy.ts in src/ directory
import { clerkMiddleware } from "@clerk/nextjs/server";
// Define public routes that don't require authentication
const publicRoutes = [
"/",
"/login",
"/login2",
"/register",
"/forgot-password",
"/reset-password",
"/pricing",
"/terms-and-conditions",
"/privacy-policy",
"/contact",
"/api/health",
"/api/webhooks/clerk",
"/api/stripe/webhook",
// Brand storefronts are public
"/tuxedo",
"/tuxedo/*",
"/indian-river-direct",
"/indian-river-direct/*",
// Error pages
"/error",
"/not-found",
];
// Export the clerkMiddleware as the default export
export default clerkMiddleware({
// Public routes configuration
publicRoutes,
// Debug mode in development
debug: process.env.NODE_ENV === "development",
});
export const config = {
matcher: [
// Skip Next.js internals and all files in the _next directory
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
// Include Clerk's auto-proxy path for authentication
"/__clerk/(.*)",
],
};