next-auth-session
v0.1.6
Published
Session management package for Next.js with JWT encryption, route protection, and React hooks
Downloads
2,390
Maintainers
Readme
next-auth-session
A simple, flexible session management library for Next.js 14+ with App Router support.
Features
- 🔐 JWT Session Encryption - Secure session storage using
jose - 🚀 Server & Client Support - Works in Server Components, Server Actions, and Client Components
- 🛡️ Route Protection - Middleware guard for public/auth/hybrid routes
- 🌍 i18n Support - Locale-aware route matching
- ⚡ Factory API - Single configuration, all functions pre-configured
- 🎣 React Hooks -
useSession,useUser,useSessionExpiryand more - 📝 TypeScript - Full type safety with generic user types
- 🔄 Auto Refresh - Automatic session refresh based on user activity
Installation
npm install next-auth-session
# or
pnpm add next-auth-session
# or
yarn add next-auth-sessionImports
// Server-side: Factory and utilities
import { NextAuthSession, route, patterns } from "next-auth-session";
// Client-side: Provider and hooks
import {
SessionProvider,
useSession,
useUser,
useSessionExpiry,
useRequireAuth
} from "next-auth-session/client";Quick Start
1. Set Environment Variable
SESSION_SECRET=your-secret-key-at-least-32-charactersGenerate a secret:
openssl rand -base64 322. Create Auth Configuration
Create lib/auth.ts:
import { NextAuthSession, route, patterns } from "next-auth-session";
export const {
// Session operations
createSession,
getSession,
updateSession,
refreshSession,
deleteSession,
isAuthenticated,
// Utilities
encryptSession,
decryptSession,
isSessionExpired,
shouldRefreshSession,
// Middleware & Handlers
middleware,
handlers,
// Server Actions - use with client components
actions,
// Config
config,
clientConfig,
} = NextAuthSession({
session: {
sessionExpiry: 60 * 60 * 24, // 1 day in seconds
cookieName: "session",
autoRefresh: true,
},
middleware: {
routes: [
route.public("/login"),
route.public("/register"),
route.auth(patterns.dashboard), // /dashboard/*
route.hybrid("/"),
],
loginPath: "/login",
homePath: "/",
},
});3. Setup API Route
Create app/api/session/[[...session]]/route.ts:
import { handlers } from "@/lib/auth";
export const { GET, POST, PATCH, DELETE } = handlers();4. Setup Middleware
Create middleware.ts:
import { middleware } from "@/lib/auth";
export default middleware();
export const config = {
matcher: ["/((?!api|_next|static|.*\\..*).*)"],
};Or with custom logic:
import { middleware } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
export default middleware((request, isAuthenticated, rule) => {
// Custom logic here
// Return NextResponse to override default behavior
// Return undefined to use default behavior
});5. Create Session (Login)
import { createSession } from "@/lib/auth";
import { redirect } from "next/navigation";
export async function loginAction(formData: FormData) {
"use server";
// Validate credentials...
const user = await validateUser(formData);
if (!user) {
return { error: "Invalid credentials" };
}
const result = await createSession(user, {
accessToken: "optional-token",
refreshToken: "optional-refresh-token",
});
if (result.success) {
redirect("/dashboard");
}
return { error: result.error };
}6. Get Session (Server Components)
import { getSession } from "@/lib/auth";
export default async function DashboardPage() {
const session = await getSession();
if (!session) {
return <div>Not authenticated</div>;
}
return <div>Welcome, {session.user.name}</div>;
}7. Client-Side Session
For client components, use the SessionProvider:
// app/providers.tsx
"use client";
import { SessionProvider } from "next-auth-session/client";
export function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}// components/UserMenu.tsx
"use client";
import { useSession } from "next-auth-session/client";
export function UserMenu() {
const { session, status, logout, refresh } = useSession();
if (status === "loading") return <div>Loading...</div>;
if (status === "unauthenticated") return <LoginButton />;
return (
<div>
<span>{session.user.name}</span>
<button onClick={logout}>Logout</button>
</div>
);
}8. Using Server Actions
The actions object contains server-ready functions for client components:
// lib/actions.ts
"use server";
import { actions } from "@/lib/auth";
export async function refreshAction() {
return actions.refresh();
}
export async function logoutAction() {
return actions.logout();
}
export async function updateAction(updates: { name?: string }) {
return actions.update({ user: updates });
}// components/RefreshButton.tsx
"use client";
import { refreshAction, logoutAction } from "@/lib/actions";
export function RefreshButton() {
return (
<button onClick={() => refreshAction()}>
Refresh Session
</button>
);
}Configuration Options
Session Config
NextAuthSession({
session: {
// Required: Secret key (defaults to SESSION_SECRET env var)
secretKey: process.env.SESSION_SECRET,
// Session expiry in seconds (default: 7 days)
sessionExpiry: 60 * 60 * 24 * 7,
// Auto-refresh threshold in seconds (default: 1 day)
refreshThreshold: 60 * 60 * 24,
// Cookie name (default: "session")
cookieName: "session",
// Auto-refresh session on read (default: true)
autoRefresh: true,
// Secure cookie in production (default: true in production)
secureCookie: process.env.NODE_ENV === "production",
// SameSite cookie attribute (default: "lax")
sameSite: "lax",
// Custom validation callback
onValidate: async (session) => {
// Return true if valid, false to invalidate
return true;
},
// Custom refresh callback
onRefresh: async (session) => {
// Return updated session or null
return session;
},
},
});Middleware Config
NextAuthSession({
middleware: {
// Route rules
routes: [
route.public("/login"), // Only for unauthenticated users
route.auth("/dashboard/*"), // Only for authenticated users
route.hybrid("/"), // For everyone
],
// Default route type (default: "hybrid")
defaultType: "hybrid",
// Redirect paths
loginPath: "/login",
homePath: "/",
// i18n locale prefixes to strip
locales: ["en", "ar"],
// Custom authorization
authorize: async (isAuthenticated, request, rule) => {
// Return NextResponse to override
// Return undefined for default behavior
},
},
});Route Types
public: Only accessible to unauthenticated users. Authenticated users are redirected tohomePath.auth: Only accessible to authenticated users. Unauthenticated users are redirected tologinPath.hybrid: Accessible to everyone. No redirects.
Route Patterns
import { route, patterns } from "next-auth-session";
// Built-in patterns
patterns.api // "/api/*"
patterns.dashboard // "/dashboard/*"
patterns.admin // "/admin/*"
patterns.settings // "/settings/*"
patterns.profile // "/profile/*"
// Custom patterns
route.auth("/dashboard/*") // Matches /dashboard and /dashboard/anything
route.public("/login") // Exact match
route.hybrid("/blog*") // Matches /blog, /blog/post, /blogsSession Object
interface Session<T extends User> {
user: T;
accessToken?: string;
refreshToken?: string;
expiresAt: number; // Unix timestamp in ms
expiryDate: string; // ISO date string
createdAt: number; // Unix timestamp in ms
lastActivity: number; // Unix timestamp in ms
}
interface User {
id: string;
email?: string;
name?: string;
image?: string;
[key: string]: unknown;
}API Reference
Factory Return Values
| Function | Description |
|----------|-------------|
| createSession(user, tokens?) | Create a new session |
| getSession() | Get current session |
| updateSession(updates) | Update session data |
| refreshSession() | Extend session expiry |
| deleteSession() | Delete session |
| isAuthenticated() | Check if user is authenticated |
| middleware(customHandler?) | Create middleware function |
| handlers() | Create API route handlers |
| encryptSession(session) | Encrypt session to JWT |
| decryptSession(token) | Decrypt JWT to session |
| isSessionExpired(session) | Check if session is expired |
| shouldRefreshSession(session) | Check if session needs refresh |
| actions | Server actions: { refresh, update, logout } |
| config | The resolved session config |
| clientConfig | Pre-configured props for SessionProvider |
Client Hooks
import { useSession, useUser, useSessionExpiry, useRequireAuth } from "next-auth-session/client";
// useSession - Full session access
const {
session, // Current session or null
status, // "loading" | "authenticated" | "unauthenticated"
refresh, // Refresh session function
update, // Update session function
} = useSession();
// useUser - User-focused access
const {
user, // User object or null
isAuthenticated, // Boolean
isLoading, // Boolean
} = useUser();
// useSessionExpiry - Track session expiry
const {
expiresAt, // Unix timestamp or null
expiresIn, // Milliseconds until expiry
isExpiringSoon, // True if expiring within 1 hour
recalculate, // Manually recalculate values
} = useSessionExpiry();
// useRequireAuth - Redirect if not authenticated
const { isLoading, isAuthenticated } = useRequireAuth("/login");SessionProvider Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | required | Child components |
| initialSession | Session \| null | null | Pre-fetched session from server |
| fetchOnMount | boolean | true | Fetch session when component mounts |
| refreshInterval | number | undefined | Auto-refresh interval in ms |
| refetchOnPathChange | boolean | true | Refetch session on route change |
| autoActivityRefresh | boolean | true | Auto-refresh based on refreshThreshold when user is active |
| activityRefreshInterval | number | undefined | Custom activity refresh interval (overrides server) |
| onSessionChange | (session) => void | undefined | Callback when session changes |
The SessionProvider automatically fetches refreshThreshold from the API and uses it for activity-based session refresh. When a user is active (mouse, keyboard, touch, scroll), the session is refreshed at the configured threshold interval.
useSessionExpiry Hook
import { useSessionExpiry } from "next-auth-session/client";
function SessionTimer() {
const { expiresAt, expiresIn, isExpiringSoon, recalculate } = useSessionExpiry();
// Manually recalculate expiry time
useEffect(() => {
const interval = setInterval(recalculate, 1000);
return () => clearInterval(interval);
}, [recalculate]);
if (!expiresAt) return null;
return (
<div>
{isExpiringSoon && <span>Session expiring soon!</span>}
<span>Expires in: {Math.floor((expiresIn ?? 0) / 1000)}s</span>
</div>
);
}| Property | Type | Description |
|----------|------|-------------|
| expiresAt | number \| null | Unix timestamp when session expires |
| expiresIn | number \| null | Milliseconds until expiry |
| isExpiringSoon | boolean | True if expiring within 1 hour |
| recalculate | () => void | Manually recalculate expiry values |
Advanced Usage
Custom User Type
import type { User, Session } from "next-auth-session";
interface MyUser extends User {
id: string;
email: string;
name: string;
role: "admin" | "user";
permissions: string[];
}
// Type-safe session operations
const session = await getSession<MyUser>();
session?.user.role; // "admin" | "user"With i18n (next-intl)
// lib/auth.ts
export const { middleware } = NextAuthSession({
middleware: {
routes: [
route.public("/login"),
route.auth("/dashboard/*"),
],
locales: ["en", "ar", "es"], // Strip locale prefix when matching
loginPath: "/login",
homePath: "/dashboard",
},
});
// middleware.ts
import { middleware } from "@/lib/auth";
import createIntlMiddleware from "next-intl/middleware";
const intlMiddleware = createIntlMiddleware({
locales: ["en", "ar", "es"],
defaultLocale: "en",
});
export default async function combinedMiddleware(request: NextRequest) {
const response = intlMiddleware(request);
if (response.ok) {
const authResponse = await middleware()(request);
if (authResponse.status !== 200) {
return authResponse;
}
}
return response;
}Server-Side Initial Session
// app/layout.tsx
import { getSession } from "@/lib/auth";
import { SessionProvider } from "next-auth-session/client";
export default async function RootLayout({ children }) {
const session = await getSession();
return (
<html>
<body>
<SessionProvider
initialSession={session}
fetchOnMount={false}
>
{children}
</SessionProvider>
</body>
</html>
);
}Types
interface User {
id: string;
[key: string]: unknown;
}
interface Session<T extends User = User> {
user: T;
accessToken?: string;
refreshToken?: string;
expiresAt: number;
expiryDate: string;
createdAt: number;
lastActivity?: number;
}
interface SessionConfig {
secretKey?: string;
sessionExpiry?: number;
refreshThreshold?: number;
cookieName?: string;
autoRefresh?: boolean;
secureCookie?: boolean;
sameSite?: "strict" | "lax" | "none";
onRefresh?: (session: Session) => Promise<Session | null>;
onValidate?: (session: Session) => Promise<boolean>;
}
interface MiddlewareConfig {
routes?: RouteRule[];
defaultType?: RouteType;
loginPath?: string;
homePath?: string;
locales?: string[];
authorize?: (isAuth: boolean, req: NextRequest, rule?: RouteRule) => NextResponse | void;
}
type RouteType = "public" | "auth" | "hybrid";
interface RouteRule {
pattern: string;
type: RouteType;
}License
MIT
