feat(auth): proactive session expiry redirect across all delivery paths

- Split auth config into auth.config.ts (edge-safe, no argon2) and auth-edge.ts
  for middleware use; auth.ts now spreads the shared config
- Middleware wraps with auth() to redirect unauthenticated requests to /auth/signin
  before any page render; passes through /auth/, /api/, /invite/ paths
- SessionGuard client component watches useSession() and redirects on
  status=unauthenticated, closing the SPA navigation gap
- QueryCache + MutationCache in TRPCProvider redirect on UNAUTHORIZED tRPC errors
  without retrying; SessionProvider polls session state every 5 minutes
- Middleware tests updated for async auth wrapper and auth-edge mock

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-04-03 10:42:10 +02:00
parent ed4d4e4640
commit bf8577dbaf
8 changed files with 151 additions and 57 deletions
+4 -39
View File
@@ -8,6 +8,7 @@ import { CredentialsSignin } from "next-auth";
import { verify } from "@node-rs/argon2";
import { z } from "zod";
import { assertSecureRuntimeEnv } from "./runtime-env";
import { authConfig } from "./auth.config.js";
assertSecureRuntimeEnv();
@@ -30,7 +31,8 @@ const LoginSchema = z.object({
totp: z.string().optional(),
});
const authConfig = {
const config = {
...authConfig,
trustHost: true,
providers: [
Credentials({
@@ -277,43 +279,6 @@ const authConfig = {
});
},
},
cookies: {
sessionToken: {
name: "authjs.session-token",
options: {
httpOnly: true,
sameSite: "strict" as const,
path: "/",
secure: process.env.NODE_ENV === "production",
},
},
callbackUrl: {
name: "authjs.callback-url",
options: {
httpOnly: true,
sameSite: "strict" as const,
path: "/",
secure: process.env.NODE_ENV === "production",
},
},
csrfToken: {
name: "authjs.csrf-token",
options: {
httpOnly: true,
sameSite: "strict" as const,
path: "/",
secure: process.env.NODE_ENV === "production",
},
},
},
pages: {
signIn: "/auth/signin",
},
session: {
strategy: "jwt",
maxAge: 28800, // 8 hours absolute timeout
updateAge: 1800, // Refresh token every 30 minutes (idle timeout)
},
} satisfies NextAuthConfig;
export const { handlers, auth } = NextAuth(authConfig);
export const { handlers, auth } = NextAuth(config);