Basic Setup Template
Set up Next.js with Entity Auth in one command
Overview
The basic setup template scaffolds a complete Next.js application with Entity Auth integration out of the box. Run one command and get:
- Next.js 15+ with TypeScript and Turbopack
- Entity Auth SDK configured and initialized
- React Context provider with
useAuth()hook - Protected route middleware
- Environment variable template
- Pre-built
AuthFormUI component with tabs for login/register - shadcn/ui automatically installed and configured
One command setup
This template handles all the boilerplate. You just wire up the provider in your layout and start building.
Quick Start
Run the setup command
npx @entityauth/cli@latest setup run entity-auth --node entityauth:setup:basicThis will:
- Run
npx create-next-app@latestwith TypeScript and Turbopack - Install and configure shadcn/ui with all components
- Create an
entity-auth/directory with all integration files - Include a
components/subdirectory with theAuthFormUI component - Generate a
.env.local.examplewith placeholder values
Configure environment variables
Copy the example file and fill in your workspace credentials:
cp entity-auth/.env.local.example .env.localGet your credentials from the Entity Auth Dashboard:
- Create or select a workspace
- Go to the Setup tab
- Copy your tenant ID and URL
Your .env.local should look like:
ENTITY_AUTH_WORKSPACE_TENANT_ID=ws_prod_abc123
NEXT_PUBLIC_ENTITY_AUTH_WORKSPACE_TENANT_ID=ws_prod_abc123
NEXT_PUBLIC_ENTITY_AUTH_URL=https://ws_prod_abc123.entity-auth.comInstall the Entity Auth SDK and React package
pnpm add @entityauth/auth-client @entityauth/reactInitialize and wrap your app with EntityAuthProvider
Update your root layout to include the provider from @entityauth/react and initialize the SDK once:
import { init as initEA } from '@entityauth/auth-client';
import { EntityAuthProvider } from '@entityauth/react';
initEA({
workspaceTenantId: process.env.NEXT_PUBLIC_ENTITY_AUTH_WORKSPACE_TENANT_ID!,
baseURL: process.env.NEXT_PUBLIC_ENTITY_AUTH_URL,
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<EntityAuthProvider>
{children}
</EntityAuthProvider>
</body>
</html>
);
}Use the useAuth hook in your components
'use client';
import { useAuth } from '../../entity-auth/auth-provider';
export default function Dashboard() {
const { user, isAuthenticated, loading, logout } = useAuth();
if (loading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <div>Please log in</div>;
}
return (
<div>
<h1>Welcome, {user?.email}</h1>
<button onClick={logout}>Logout</button>
</div>
);
}What's Included
The setup creates an entity-auth/ directory with these files:
auth-client.tsx
Initializes the Entity Auth SDK and exports a singleton client instance.
import { EntityAuthClient, init as initEA } from '@entityauth/auth-client';
initEA({
workspaceTenantId: process.env.NEXT_PUBLIC_ENTITY_AUTH_WORKSPACE_TENANT_ID || '',
baseURL: process.env.NEXT_PUBLIC_ENTITY_AUTH_URL,
});
export const entityAuthClient = new EntityAuthClient({
baseURL: process.env.NEXT_PUBLIC_ENTITY_AUTH_URL,
});
auth-provider.tsx
React Context provider with authentication state and methods:
- State:
user,isAuthenticated,loading - Methods:
login(email, password),register(email, password),logout(),refresh() - Client: Direct access via
clientproperty - Hooks:
useAuth()anduseRequireAuth()
Features:
- Automatically loads user on mount
- Listens for token changes (logout, refresh)
- Auto-login after registration
- Error handling with user-friendly messages
middleware.ts
Next.js middleware for route protection:
- Redirects unauthenticated users from protected routes (
/dashboard,/profile,/settings) - Redirects authenticated users away from auth pages (
/login,/register) - Checks
ea_refreshcookie for authentication state - Preserves redirect parameter for post-login navigation
.env.local.example
Template with all required environment variables. Just replace placeholders with your actual values from the dashboard.
components/auth-form.tsx
A production-ready authentication UI component built with shadcn/ui that includes:
- Tabbed interface: Switch between login and register views
- Fully integrated: Works seamlessly with the EntityAuthClient
- Customizable: Props for redirects, titles, branding, error/success handlers
- Auto-redirect: Automatically redirects authenticated users
- Form validation: Email and password validation
- Error handling: Built-in error display with customizable handlers
Key features:
- Uses shadcn/ui components (
Button,Card,Input,Label,Tabs) - Auto-login after registration
- Logout button when already authenticated
- Customizable brand name, titles, and descriptions
- Optional header and back link
- Type-safe with full TypeScript support
Usage Examples
Using the Pre-built AuthForm Component
The easiest way to add authentication to your app is to use the included AuthForm component:
'use client';
import { AuthForm } from '../../entity-auth/components/auth-form';
import { entityAuthClient } from '../../entity-auth/auth-client';
export default function AuthPage() {
return (
<AuthForm
client={entityAuthClient}
redirectTo="/dashboard"
title="Welcome"
description="Sign in to your account or create a new one"
brandName="My App"
/>
);
}
Custom Login Form
If you need a custom UI, you can build your own form using the useAuth hook:
'use client';
import { useAuth } from '../entity-auth/auth-provider';
import { useState } from 'react';
export default function LoginPage() {
const { login, isAuthenticated } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await login(email, password);
if (!result.success) {
setError(result.error || 'Login failed');
}
};
if (isAuthenticated) {
return <div>Already logged in!</div>;
}
return (
<form onSubmit={handleSubmit}>
{error && <div style={{ color: 'red' }}>{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Login</button>
</form>
);
}
Registration Form
'use client';
import { useAuth } from '../entity-auth/auth-provider';
import { useState } from 'react';
export default function RegisterPage() {
const { register } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await register(email, password);
if (!result.success) {
setError(result.error || 'Registration failed');
}
};
return (
<form onSubmit={handleSubmit}>
{error && <div style={{ color: 'red' }}>{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Register</button>
</form>
);
}
Protected Route
'use client';
import { useRequireAuth } from '../entity-auth/auth-provider';
export default function ProtectedPage() {
const { isAuthenticated, loading } = useRequireAuth();
if (loading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <div>Access denied. Please log in.</div>;
}
return <div>Protected content here</div>;
}
Using the Client Directly
For advanced use cases, access the client directly:
'use client';
import { useAuth } from '../entity-auth/auth-provider';
export default function AdvancedPage() {
const { client } = useAuth();
const handleSetUsername = async () => {
try {
await client.setUsername({ username: 'newusername' });
console.log('Username set!');
} catch (error) {
console.error('Failed to set username:', error);
}
};
const handleSwitchOrg = async (orgId: string) => {
try {
await client.switchOrg({ orgId });
console.log('Switched organization!');
} catch (error) {
console.error('Failed to switch org:', error);
}
};
return (
<div>
<button onClick={handleSetUsername}>Set Username</button>
<button onClick={() => handleSwitchOrg('org_123')}>Switch Org</button>
</div>
);
}
Customization
Add More Protected Routes
Edit the PROTECTED_ROUTES array in middleware.ts:
const PROTECTED_ROUTES = ['/dashboard', '/profile', '/settings', '/admin'];
Modify Auth Routes
Update the AUTH_ROUTES array to change which pages authenticated users can't access:
const AUTH_ROUTES = ['/login', '/register', '/forgot-password'];
Extend the Auth Context
Add custom methods to the provider by editing auth-provider.tsx:
const getOrganizations = useCallback(async () => {
try {
const orgs = await entityAuthClient.getUserOrganizations();
return orgs;
} catch (error) {
console.error('Failed to get organizations:', error);
return [];
}
}, []);
// Add to context value
return (
<AuthContext.Provider
value={{
// ... existing values
getOrganizations,
}}
>
{children}
</AuthContext.Provider>
);
AuthForm Component Props
The AuthForm component accepts these props for customization:
| Prop | Type | Default | Description |
|---|---|---|---|
client | EntityAuthClient | required | The Entity Auth client instance |
redirectTo | string | "/dashboard" | Path to redirect after successful auth |
showHeader | boolean | true | Show header with branding |
showBackLink | boolean | true | Show back link at bottom |
backLinkHref | string | "/" | Href for the back link |
title | string | "Dashboard Access" | Card title text |
description | string | "Authenticate to access..." | Card description text |
brandName | string | "Entity Auth" | Brand name in header/footer |
onError | function | undefined | Custom error handler (error, action) => void |
onSuccess | function | undefined | Custom success handler (action) => void |
Next Steps
- Read the Authentication guide for advanced patterns
- Explore Organizations for multi-tenant features
- Check out the Framework Integration guide for more hooks
Ready to go
Your Next.js app is now fully integrated with Entity Auth. Just wrap your layout with the provider and start using the useAuth() hook!