A reusable password protection module for Next.js applications with secure token-based authentication
npm install nextjs-password-protectA reusable password protection module for Next.js applications that provides a beautiful, customizable password protection screen with secure token-based authentication.
``bash`
npm install nextjs-password-protector
yarn add nextjs-password-protector
pnpm add nextjs-password-protect
- š Simple password-based authentication
- šØ Modern, responsive UI that automatically inherits your application's theme
- š Automatic theme support (light/dark mode) via CSS variables
- š¼ļø Custom brand logo support
- š¾ Optional localStorage persistence
- š Secure token-based authentication (prevents localStorage manipulation)
- āļø Highly configurable
- š¦ Reusable package structure
- šÆ TypeScript support
`bash`
npm install nextjs-password-protect
Copy the API route template to your Next.js app:
Option A: Copy from node_modules
`bash`
cp node_modules/nextjs-password-protect/api-route-template.ts app/api/auth/verify/route.ts
Option B: Create manually
Create app/api/auth/verify/route.ts:
`typescript
import { NextRequest, NextResponse } from "next/server";
import { createHash, randomBytes } from "crypto";
// In-memory token store (in production, consider using Redis or a database)
// Format: { tokenHash: { expiresAt: number, createdAt: number } }
const tokenStore = new Map
// Clean up expired tokens periodically
setInterval(() => {
const now = Date.now();
for (const [tokenHash, data] of tokenStore.entries()) {
if (data.expiresAt < now) {
tokenStore.delete(tokenHash);
}
}
}, 60000); // Clean up every minute
function generateToken(): string {
return randomBytes(32).toString("hex");
}
function createTokenHash(token: string): string {
return createHash("sha256").update(token).digest("hex");
}
export async function POST(request: NextRequest) {
try {
const { password, token: requestToken } = await request.json();
// Token validation endpoint (prevents localStorage manipulation)
if (requestToken) {
const tokenHash = createTokenHash(requestToken);
const tokenData = tokenStore.get(tokenHash);
if (!tokenData) {
return NextResponse.json(
{ success: false, error: "Invalid token" },
{ status: 401 }
);
}
if (tokenData.expiresAt < Date.now()) {
tokenStore.delete(tokenHash);
return NextResponse.json(
{ success: false, error: "Token expired" },
{ status: 401 }
);
}
// Token is valid, refresh expiration
const expiresIn = 24 60 60 * 1000; // 24 hours
tokenData.expiresAt = Date.now() + expiresIn;
return NextResponse.json({ success: true, valid: true });
}
// Password validation endpoint
if (!password) {
return NextResponse.json(
{ success: false, error: "Password is required" },
{ status: 400 }
);
}
// Get password from server-side environment variable (NOT NEXT_PUBLIC)
const correctPassword = process.env.APP_PASSWORD || "demo123";
if (password !== correctPassword) {
return NextResponse.json(
{ success: false, error: "Incorrect password" },
{ status: 401 }
);
}
// Generate secure token
const newToken = generateToken();
const tokenHash = createTokenHash(newToken);
const expiresIn = 24 60 60 * 1000; // 24 hours
tokenStore.set(tokenHash, {
expiresAt: Date.now() + expiresIn,
createdAt: Date.now(),
});
return NextResponse.json({
success: true,
token: newToken, // Return plain token to client (hash is stored server-side)
});
} catch (error) {
return NextResponse.json(
{ success: false, error: "Invalid request" },
{ status: 400 }
);
}
}
`
Create .env.local in your project root:
`env`
APP_PASSWORD=your-secure-password-here
Create tailwind.config.ts:
`ts
import type { Config } from "tailwindcss";
const Config = {
content: [
"./node_modules/nextjs-password-protect/*/.{js,ts,jsx,tsx}", // Include the Password Protect files
],
darkMode: "class",
};
export default Config;
`
`css`
@config "./../tailwind.config.ts";
Wrap your application content in app/layout.tsx:
`tsx
import { PasswordProtectWrapper } from "nextjs-password-protect";
export default function RootLayout({ children }) {
return (
Basic Usage
`tsx
import { PasswordProtectWrapper } from "nextjs-password-protect";export default function RootLayout({ children }) {
return (
config={
{
// Password validated via /api/auth/verify endpoint
// Set APP_PASSWORD in .env.local (without NEXT_PUBLIC prefix)
}
}
>
{children}
);
}
`API Reference
$3
The main wrapper component that protects your application.
Props:
-
children: ReactNode - The content to protect
- config: PasswordProtectConfig - Configuration object$3
| Property | Type | Default | Description |
| -------------- | --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
password | string | undefined | ā ļø Deprecated - Password for client-side validation (visible in bundle). Omit to use secure server-side validation. |
| apiEndpoint | string | "/api/auth/verify" | API endpoint for server-side password validation |
| logo | string \| ReactNode | undefined | Optional brand logo (URL/path or React component) |
| title | string | "Password Protected" | Title text for the password screen |
| description | string | "Please enter the password..." | Description text |
| placeholder | string | "Enter password" | Placeholder for password input |
| errorMessage | string | "Incorrect password..." | Error message on wrong password |
| classNames | object | undefined | Custom class names for UI elements. Keys: wrapper, container, logo, heading, description, input, button, errormessage |
| onSuccess | () => void | undefined | Callback on successful authentication |
| onError | () => void | undefined | Callback on failed authentication |
| storageKey | string | "password-protect-auth" | localStorage key for persistence |
| persistAuth | boolean | true | Whether to persist auth state |Styling
The component automatically inherits your application's theme using CSS variables:
-
--background: Background color (defaults to white)
- --foreground: Text color (defaults to dark gray)
- --border: Border color (defaults to light gray)The component will automatically adapt to your application's theme (light/dark mode) without any additional configuration. You can customize the appearance using the
classNames prop to target specific UI elements, or by overriding styles.classNames object keys:
-
wrapper: Outer wrapper
- container: Main container
- logo: Logo element
- heading: Heading text
- description: Description text
- input: Input field
- button: Submit button
- errormessage: Error messageNote: If your application uses different CSS variable names, you can override styles using the
classNames prop or by providing custom CSS.Security Notes
ā ļø Important Security Considerations:
1. Token-Based Authentication: The package uses secure token-based authentication to prevent localStorage manipulation attacks. Tokens are:
- Generated server-side using cryptographically secure random bytes
- Hashed and stored server-side (only hash is stored, not the token)
- Validated on every page load/refresh
- Automatically expire after 24 hours
- Cannot be forged by manually setting localStorage
2. Server-Side Validation (Recommended): By default, the package uses server-side validation via API route. The password is stored in
APP_PASSWORD (without NEXT_PUBLIC prefix) and never exposed to the client bundle.3. Client-Side Protection Limitation: If using the legacy
password prop in config, the password will be visible in the JavaScript bundle and localStorage can be manually manipulated. This is only suitable for casual access control, NOT for sensitive data.4. Token Storage: Tokens are stored in-memory on the server. For production with multiple server instances, consider using Redis or a database for token storage.
5. Password Storage: Never commit passwords to version control. Always use environment variables in
.env.local (which should be in .gitignore`).6. HTTPS: Always use HTTPS in production to prevent password and token interception during transmission.
7. Not for Sensitive Data: This is a basic password protection. For production applications with sensitive data, implement proper authentication systems (OAuth, JWT, etc.).
See EXAMPLE.md for detailed usage examples.
See SETUP.md for detailed setup instructions.
MIT