@tidecloak/nextjs
Version:
TideCloak nextjs SDK
86 lines (85 loc) • 3.25 kB
JavaScript
import { NextResponse } from 'next/server';
import { verifyTideCloakToken } from '@tidecloak/verify';
import { normalizeProtectedRoutes } from './routerMatcher';
const DEFAULTS = {
protectedRoutes: {},
onRequest: undefined,
onSuccess: undefined,
};
/**
* Returns a Next.js Edge Middleware function enforcing TideCloak auth.
*
* Example usage in your `middleware.ts`:
*
* ```ts
* import tidecloakConfig from './tidecloakAdapter.json'
* import { createTideMiddleware } from 'tidecloak-nextjs/server/tidecloakMiddleware'
*
* export default createTideMiddleware({
* config: tidecloakConfig,
* protectedRoutes: {
* '/admin/*': ['admin'],
* '/api/private/*': ['user']
* }
* })
*
* export const config = {
* matcher: [
* '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico)).*)',
* '/(api|trpc)(.*)'
* ],
* runtime: 'edge'
* }
* ```
*/
export function createTideCloakMiddleware(opts) {
const settings = { ...DEFAULTS, ...opts };
// Prepare arrays of test functions for protected routes
const protectedTests = normalizeProtectedRoutes(settings.protectedRoutes);
return async function middleware(req) {
var _a;
const path = req.nextUrl.pathname;
try {
// Extract the raw JWT from the specified cookie
const token = ((_a = req.cookies.get("kcToken")) === null || _a === void 0 ? void 0 : _a.value) || null;
// Allow custom logic before auth checks
if (settings.onRequest) {
const result = settings.onRequest({ token }, req);
if (result)
return result;
}
// Iterate protected routes; the first match enforces a role check
for (const { test, roles } of protectedTests) {
if (test(path, req)) {
// Verify signature, issuer, and presence of at least one allowed role
const payload = await verifyTideCloakToken(settings.config, token, roles);
if (!payload) {
// Custom onFailure hook or default redirect
const result = settings.onFailure({ token }, req);
if (result)
return result;
return NextResponse.json({ error: '[TideCloak Middleware] Access forbidden: invalid token' }, { status: 403 });
}
// Custom onSuccess hook if provided
if (settings.onSuccess) {
const result = settings.onSuccess({ payload }, req);
if (result)
return result;
}
// Token valid and role check passed
return NextResponse.next();
}
}
// No protected route matched; continue
return NextResponse.next();
}
catch (err) {
// Handle unexpected errors
if (settings.onError) {
return settings.onError(err, req);
}
console.error("[TideCloak Middleware] ", err);
throw err;
}
};
}