Files
honeyDueWeb/src/middleware.ts
T
Trey t 42e7bedea4 Replace hand-rolled auth with Ory Kratos browser flows
The honeyDue Go API no longer owns identity — Ory Kratos at
NEXT_PUBLIC_KRATOS_URL does. Rewrite the web app's auth layer to use Kratos
browser self-service flows and the ory_kratos_session cookie.

- Kratos client (src/lib/kratos/): flow init/fetch/submit, whoami, logout,
  message helpers, and the useKratosFlow lifecycle hook.
- Generic flow renderer (src/components/auth/): KratosFlowForm renders
  ui.nodes (inputs, oidc social buttons, hidden csrf), KratosMessages
  surfaces flow-level messages, AuthGate guards /app via whoami.
- Auth pages (login/register/forgot-password/verify-email/reset-password)
  rewritten as Kratos login/registration/recovery/verification/settings
  flows. Password change in settings now uses the Kratos settings flow.
- Proxy + serverFetch forward the ory_kratos_session cookie to the Go API
  instead of "Authorization: Token". Deleted /api/auth/{login,logout,me}.
- Middleware does a cheap ory_kratos_session cookie pre-filter; AuthGate's
  whoami call is authoritative.
- auth store rewritten around whoami + GET /auth/me; removed dead auth API
  functions, types/auth, validations/auth, code-input.
- Added NEXT_PUBLIC_KRATOS_URL to config (.env.example) and CLAUDE.md.

npm run build passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:16:49 -05:00

63 lines
2.3 KiB
TypeScript

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
// ---------------------------------------------------------------------------
// Middleware — cheap cookie-presence route pre-filter
// ---------------------------------------------------------------------------
// Identity is owned by Ory Kratos. The authoritative session check is the
// `whoami` call done client-side by <AuthGate> (and server-side by the Go API
// for every API request). Middleware only does a cheap pre-filter on the
// presence of the `ory_kratos_session` cookie so that obviously-logged-out
// users are bounced to /login without a flash of the app shell.
//
// The cookie's mere presence does NOT guarantee a valid session (it may be
// expired) — that's why <AuthGate> still re-verifies via `whoami`.
// ---------------------------------------------------------------------------
const SESSION_COOKIE = 'ory_kratos_session';
export function middleware(request: NextRequest) {
const hasSession = Boolean(request.cookies.get(SESSION_COOKIE)?.value);
const { pathname } = request.nextUrl;
// Public paths that don't require auth.
const publicPaths = [
'/',
'/login',
'/register',
'/forgot-password',
'/reset-password',
'/verify-email',
'/demo',
'/help',
];
const isPublicPath = publicPaths.some(
(p) => pathname === p || pathname.startsWith(p + '/'),
);
const isApiPath = pathname.startsWith('/api/');
const isStaticPath =
pathname.startsWith('/_next/') ||
pathname.startsWith('/favicon') ||
pathname.match(/\.(png|jpg|jpeg|gif|svg|ico|webp|woff2?|ttf|css|js)$/);
// Skip middleware for API routes and static files.
if (isApiPath || isStaticPath) return NextResponse.next();
// No session cookie + protected path -> redirect to login.
if (!hasSession && !isPublicPath) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Has a session cookie + on the login/register pages -> send to the app.
// (AuthGate / whoami will catch the case where the cookie is stale.)
if (hasSession && (pathname === '/login' || pathname === '/register')) {
return NextResponse.redirect(new URL('/app', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};