next-token-auth 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -138,6 +138,8 @@ export const clientAuthConfig: ClientAuthConfig = {
138
138
 
139
139
  Create this file to handle login, logout, refresh, and session endpoints automatically.
140
140
 
141
+ **Important:** The file path must be exactly `app/api/auth/[action]/route.ts` — the `[action]` part is a Next.js dynamic route segment (keep the square brackets as-is). Do not rename it.
142
+
141
143
  ```ts
142
144
  // app/api/auth/[action]/route.ts
143
145
  import { createAuthHandlers } from "next-token-auth/server";
@@ -629,7 +631,7 @@ Your backend needs to implement these endpoints:
629
631
 
630
632
  Request body: whatever fields you pass to `login()` (e.g. `{ email, password }`)
631
633
 
632
- Response:
634
+ **Response option 1 — user included:**
633
635
 
634
636
  ```json
635
637
  {
@@ -643,10 +645,29 @@ Response:
643
645
 
644
646
  // Optional — used by "backend" and "hybrid" expiry strategies
645
647
  "accessTokenExpiresIn": "2d",
646
- "refreshTokenExpiresIn": "7d",
648
+ "refreshTokenExpiresIn": "7d"
649
+ }
650
+ ```
651
+
652
+ **Response option 2 — user fetched separately:**
653
+
654
+ If your login endpoint only returns tokens, omit the `user` field and configure the `me` endpoint. The library will automatically call it after login.
655
+
656
+ ```json
657
+ {
658
+ "accessToken": "eyJhbGc...",
659
+ "refreshToken": "eyJhbGc...",
660
+ "accessTokenExpiresIn": "2d",
661
+ "refreshTokenExpiresIn": "7d"
662
+ }
663
+ ```
647
664
 
648
- // Legacy field, also accepted
649
- "expiresIn": 172800
665
+ Then in your config:
666
+
667
+ ```ts
668
+ endpoints: {
669
+ login: "/auth/login",
670
+ me: "/auth/me", // ← library calls this to fetch user profile
650
671
  }
651
672
  ```
652
673
 
@@ -17,7 +17,7 @@ interface LoginInput {
17
17
  [key: string]: unknown;
18
18
  }
19
19
  interface LoginResponse<User = unknown> {
20
- user: User;
20
+ user?: User;
21
21
  accessToken: string;
22
22
  refreshToken: string;
23
23
  /** Seconds until access token expires (legacy field) */
@@ -17,7 +17,7 @@ interface LoginInput {
17
17
  [key: string]: unknown;
18
18
  }
19
19
  interface LoginResponse<User = unknown> {
20
- user: User;
20
+ user?: User;
21
21
  accessToken: string;
22
22
  refreshToken: string;
23
23
  /** Seconds until access token expires (legacy field) */
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AuthConfig, b as AuthTokens, a as AuthSession, L as LoginInput, E as ExpiryInput } from './index-Csz5lfEv.mjs';
2
- export { C as ClientAuthConfig, c as ExpiryStrategy, d as LoginResponse } from './index-Csz5lfEv.mjs';
1
+ import { A as AuthConfig, b as AuthTokens, a as AuthSession, L as LoginInput, E as ExpiryInput } from './index-cC0Csz3x.mjs';
2
+ export { C as ClientAuthConfig, c as ExpiryStrategy, d as LoginResponse } from './index-cC0Csz3x.mjs';
3
3
  export { AuthProvider, UseAuthReturn, UseRequireAuthOptions, useAuth, useRequireAuth, useSession } from './react/index.mjs';
4
4
  import 'react/jsx-runtime';
5
5
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AuthConfig, b as AuthTokens, a as AuthSession, L as LoginInput, E as ExpiryInput } from './index-Csz5lfEv.js';
2
- export { C as ClientAuthConfig, c as ExpiryStrategy, d as LoginResponse } from './index-Csz5lfEv.js';
1
+ import { A as AuthConfig, b as AuthTokens, a as AuthSession, L as LoginInput, E as ExpiryInput } from './index-cC0Csz3x.js';
2
+ export { C as ClientAuthConfig, c as ExpiryStrategy, d as LoginResponse } from './index-cC0Csz3x.js';
3
3
  export { AuthProvider, UseAuthReturn, UseRequireAuthOptions, useAuth, useRequireAuth, useSession } from './react/index.js';
4
4
  import 'react/jsx-runtime';
5
5
  import 'react';
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { C as ClientAuthConfig, a as AuthSession, L as LoginInput } from '../index-Csz5lfEv.mjs';
3
+ import { C as ClientAuthConfig, a as AuthSession, L as LoginInput } from '../index-cC0Csz3x.mjs';
4
4
 
5
5
  interface AuthProviderProps {
6
6
  config: ClientAuthConfig;
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { C as ClientAuthConfig, a as AuthSession, L as LoginInput } from '../index-Csz5lfEv.js';
3
+ import { C as ClientAuthConfig, a as AuthSession, L as LoginInput } from '../index-cC0Csz3x.js';
4
4
 
5
5
  interface AuthProviderProps {
6
6
  config: ClientAuthConfig;
@@ -1,4 +1,4 @@
1
- import { A as AuthConfig, a as AuthSession } from '../index-Csz5lfEv.mjs';
1
+ import { A as AuthConfig, a as AuthSession } from '../index-cC0Csz3x.mjs';
2
2
 
3
3
  type NextRequest$2 = {
4
4
  cookies: {
@@ -1,4 +1,4 @@
1
- import { A as AuthConfig, a as AuthSession } from '../index-Csz5lfEv.js';
1
+ import { A as AuthConfig, a as AuthSession } from '../index-cC0Csz3x.js';
2
2
 
3
3
  type NextRequest$2 = {
4
4
  cookies: {
@@ -320,8 +320,18 @@ function createAuthHandlers(config) {
320
320
  const maxAge = tokens.refreshTokenExpiresAt ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1e3) : 604800;
321
321
  const secure = config.token.secure !== false ? "Secure; " : "";
322
322
  const sameSite = config.token.sameSite ?? "lax";
323
+ let user = data.user ?? null;
324
+ if (!user && config.endpoints.me) {
325
+ try {
326
+ const meRes = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {
327
+ headers: { Authorization: `Bearer ${tokens.accessToken}` }
328
+ });
329
+ if (meRes.ok) user = await meRes.json();
330
+ } catch {
331
+ }
332
+ }
323
333
  return NextResponse.json(
324
- { ok: true, user: data.user },
334
+ { ok: true, user },
325
335
  {
326
336
  headers: {
327
337
  "Set-Cookie": [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true, user: data.user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n // Fetch user from /me endpoint if not included in login response\n let user: User | null = data.user ?? null;\n if (!user && config.endpoints.me) {\n try {\n const meRes = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (meRes.ok) user = (await meRes.json()) as User;\n } catch {\n // User fetch failed — continue without user data\n }\n }\n\n return NextResponse.json(\n { ok: true, user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
@@ -318,8 +318,18 @@ function createAuthHandlers(config) {
318
318
  const maxAge = tokens.refreshTokenExpiresAt ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1e3) : 604800;
319
319
  const secure = config.token.secure !== false ? "Secure; " : "";
320
320
  const sameSite = config.token.sameSite ?? "lax";
321
+ let user = data.user ?? null;
322
+ if (!user && config.endpoints.me) {
323
+ try {
324
+ const meRes = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {
325
+ headers: { Authorization: `Bearer ${tokens.accessToken}` }
326
+ });
327
+ if (meRes.ok) user = await meRes.json();
328
+ } catch {
329
+ }
330
+ }
321
331
  return NextResponse.json(
322
- { ok: true, user: data.user },
332
+ { ok: true, user },
323
333
  {
324
334
  headers: {
325
335
  "Set-Cookie": [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true, user: data.user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n // Fetch user from /me endpoint if not included in login response\n let user: User | null = data.user ?? null;\n if (!user && config.endpoints.me) {\n try {\n const meRes = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (meRes.ok) user = (await meRes.json()) as User;\n } catch {\n // User fetch failed — continue without user data\n }\n }\n\n return NextResponse.json(\n { ok: true, user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-token-auth",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Production-grade authentication library for Next.js (App Router & Pages Router)",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",