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

  1. Clone the Repository: Clone the repository using the following command:

    git clone https://github.com/your-repo/your-project.git
    
    code
  2. Navigate to the Project Directory: Use the cd command to navigate to your project directory:

    cd your-project
    
    code
  3. Install Dependencies: Install the required dependencies using npm or yarn:

    npm install
    # or
    yarn install
    
    code

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