getToken
getToken guide for our application.
Overview
The getToken server action creates and sets a JWT token for authenticated users. This function is essential for implementing authentication in your Next.js application.
// Server action - getToken
'use server';
import { cookies } from 'next/headers';
import { SignJWT } from 'jose';
import { nanoid } from 'nanoid';
interface TokenPayload {
userId: string;
email: string;
role: 'user' | 'admin';
}
export async function getToken(payload: TokenPayload) {
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
if (!secret) {
throw new Error('JWT_SECRET environment variable not set');
}
const token = await new SignJWT({
sub: payload.userId,
email: payload.email,
role: payload.role,
})
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('24h')
.setJti(nanoid())
.sign(secret);
// Set the cookie
cookies().set({
name: 'token',
value: token,
httpOnly: true,
path: '/',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24, // 24 hours
sameSite: 'strict',
});
return token;
}
code
Prerequisites
Before proceeding, ensure you have the following:
- Node.js installed
- Basic knowledge of command-line interface (CLI)
- A code editor (e.g., VSCode)
Installation Steps
-
Clone the Repository: Clone the repository using the following command:
git clone https://github.com/your-repo/your-project.gitcode -
Navigate to the Project Directory: Use the
cdcommand to navigate to your project directory:cd your-projectcode -
Install Dependencies: Install the required dependencies using npm or yarn:
npm install # or yarn installcode
Client Usage
Here's an example of using the getToken function in a login form:
// Usage in login form
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { getToken } from '@/actions/auth';
export default function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
try {
// Authenticate user (example - in real app, verify credentials)
const userData = {
userId: '123',
email: email,
role: 'user'
};
await getToken(userData);
router.push('/dashboard');
} catch (err) {
setError('Failed to login. Please check your credentials.');
}
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
{error && <div className="text-red-600">{error}</div>}
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full p-2 border rounded"
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full p-2 border rounded"
/>
</div>
<button type="submit" className="w-full py-2 bg-blue-600 text-white rounded">
Login
</button>
</form>
);
}
code
Additional Information
For more details, please refer to our official documentation.
Remember:
Lorem ipsum dolor sit amet consectetur adipisicing elit Numquam iste dolorum tempore consectetur explicabo.
Server Action Implementation
// app/actions/auth.ts
'use server'
import { cookies } from 'next/headers';
import { sign } from 'jsonwebtoken';
import { z } from 'zod';
// Validate user credentials
const credentialsSchema = z.object({
email: z.string().email('Invalid email format'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
// User data returned from database
interface UserData {
id: string;
email: string;
name: string;
role: string;
passwordHash: string;
}
export async function getToken(formData: FormData) {
// Parse and validate form data
const rawCredentials = {
email: formData.get('email'),
password: formData.get('password'),
};
const validationResult = credentialsSchema.safeParse(rawCredentials);
if (!validationResult.success) {
return { error: 'Invalid credentials format' };
}
const { email, password } = validationResult.data;
try {
// In a real implementation, you would:
// 1. Look up the user in your database
// 2. Verify the password hash
// 3. Generate a token if authentication succeeds
// This is a simplified example - replace with your actual authentication logic
const user = await getUserByEmail(email);
if (!user || !(await verifyPassword(password, user.passwordHash))) {
return { error: 'Invalid email or password' };
}
// Generate JWT token
const token = generateToken(user);
// Set cookie
cookies().set({
name: 'token',
value: token,
httpOnly: true,
path: '/',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24, // 24 hours
sameSite: 'strict',
});
return { success: true };
} catch (error) {
console.error('Authentication error:', error);
return { error: 'Authentication failed' };
}
}
// Helper function to generate JWT token
function generateToken(user: UserData): string {
const secret = process.env.JWT_SECRET;
if (!secret) {
throw new Error('JWT_SECRET is not defined');
}
return sign(
{
sub: user.id,
email: user.email,
name: user.name,
role: user.role,
},
secret,
{ expiresIn: '24h' }
);
}
// Example database functions (implement these with your actual database)
async function getUserByEmail(email: string): Promise<UserData | null> {
// Replace with your database query
return null;
}
async function verifyPassword(password: string, hash: string): Promise<boolean> {
// Replace with your password verification (bcrypt, etc.)
return false;
}
code
Login Form Component
// app/components/LoginForm.tsx
'use client'
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { getToken } from '@/app/actions/auth';
export default function LoginForm() {
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const router = useRouter();
async function handleSubmit(formData: FormData) {
setIsLoading(true);
setError(null);
try {
const result = await getToken(formData);
if ('error' in result) {
setError(result.error);
} else {
router.push('/dashboard');
router.refresh(); // Refresh to update UI based on new auth state
}
} catch (err) {
setError('An unexpected error occurred');
console.error(err);
} finally {
setIsLoading(false);
}
}
return (
<div className="w-full max-w-md mx-auto p-6 bg-card rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-6">Log In</h2>
{error && (
<div className="mb-4 p-3 bg-destructive/15 border border-destructive rounded text-sm text-destructive">
{error}
</div>
)}
<form action={handleSubmit} className="space-y-4">
<div className="space-y-2">
<label htmlFor="email" className="block text-sm font-medium">
Email
</label>
<input
id="email"
name="email"
type="email"
required
className="w-full p-2 border rounded-md"
/>
</div>
<div className="space-y-2">
<label htmlFor="password" className="block text-sm font-medium">
Password
</label>
<input
id="password"
name="password"
type="password"
required
className="w-full p-2 border rounded-md"
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full py-2 px-4 bg-primary text-primary-foreground rounded-md disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Log In'}
</button>
</form>
</div>
);
}
code