Protect Pages and Server Components

Learn how to protect your pages and Server Components so only authenticated users can access them.

In this section, you'll configure your authentication middleware to automatically protect specific pages in your application. The middleware validates authentication on every request and redirects unauthenticated users to login.

Add Protected Pages to Auth Middleware

The auth middleware automatically protects page-level Server Components. These are the page.tsx files that define your page routes (e.g., src/app/dashboard/page.tsx, src/app/settings/page.tsx). It does not directly protect the following:

  • Nested Server Components within these pages (covered separately below)
  • Server Actions (covered separately later in this guide)

Update your requireAuth configuration in src/wristband.ts to specify which pages require authentication:

// src/wristband.ts

import { NextRequest } from 'next/server';
import { createWristbandAuth, getSessionFromRequest, SessionOptions } from '@wristband/nextjs-auth';

export const wristbandAuth = createWristbandAuth({
  clientId: '<WRISTBAND_CLIENT_ID>',
  clientSecret: '<WRISTBAND_CLIENT_SECRET>',
  wristbandApplicationVanityDomain: '<WRISTBAND_APPLICATION_VANITY_DOMAIN>',
});

const sessionOptions: SessionOptions = {
  secrets: '<your-generated-secret>'
};

export function getRequestSession(request: NextRequest) {
  return getSessionFromRequest(request, sessionOptions);
}

// NEW: Add Protected page-level Server Components to the auth middleware.
export const requireAuth = wristbandAuth.createMiddlewareAuth({
  authStrategies: ['SESSION'],
  sessionConfig: {
    sessionOptions,
    sessionEndpoint: '/api/auth/session'
  },
  protectedPages: [
    '/',            // Protect home page
    '/dashboard',   // Protect dashboard page
    '/settings(.*)' // Protect all settings pages (e.g., /settings/profile, /settings/billing)
  ],
});

When a user tries to access a page protected by the auth middleware, it will validate the user's session:

  • If the user has an authenticated session, the page loads normally.
  • If the user does not have an authenticated session, they're redirected to your application's Login Endpoint.

Manual Page Protection in Server Components (Optional)

If you need more granular control or special handling for specific pages, you can manually check the session in Server Components instead of relying solely on middleware.

ℹ️

Note: Middleware protection alone is typically sufficient for most use cases.

Create Server Component Session Helper Function

First, add a getServerComponentSession() helper function to your src/wristband.ts. This function provides read-only access to session data in Server Components.

💡

Why Read-Only?

Server Components render during the RSC (React Server Components) phase before the HTTP response is finalized, so they cannot modify cookies or response headers. This helper provides read-only access to the session for authentication checks and data retrieval.

For session mutations (updating or destroying sessions), use Server Actions or API routes instead.

// src/wristband.ts

import { NextRequest } from 'next/server';
import {
  createWristbandAuth,
  getReadOnlySessionFromCookies,
  getSessionFromRequest,
  NextJsCookieStore,
  SessionOptions,
} from '@wristband/nextjs-auth';

export const wristbandAuth = createWristbandAuth({
  clientId: '<WRISTBAND_CLIENT_ID>',
  clientSecret: '<WRISTBAND_CLIENT_SECRET>',
  wristbandApplicationVanityDomain: '<WRISTBAND_APPLICATION_VANITY_DOMAIN>',
});

const sessionOptions: SessionOptions = {
  secrets: '<your-generated-secret>'
};

export function getRequestSession(request: NextRequest) {
  return getSessionFromRequest(request, sessionOptions);
}

export const requireAuth = wristbandAuth.createMiddlewareAuth({
  authStrategies: ['SESSION'],
  sessionConfig: {
    sessionOptions,
    sessionEndpoint: '/api/auth/session'
  },
  protectedPages: ['/', '/dashboard', '/settings(.*)'],
});

// NEW: Helper function that can be used to retrieve a read-only 
// instance of the session within Server Components.
export function getServerComponentSession(cookieStore: NextJsCookieStore) {
  return getReadOnlySessionFromCookies(cookieStore, sessionOptions);
}

Page-level Server Component Protection

You can manually check the session in your page.tsx file and optionally leverage Next.js's redirect() function to send unauthenticated users to your Login Endpoint.

// src/app/analytics/page.tsx

import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { getServerComponentSession } from 'src/wristband';

export default async function AnalyticsPage() {
  const cookieStore = await cookies();
  const session = await getServerComponentSession(cookieStore);

  // Redirect if not authenticated
  if (!session.isAuthenticated) {
    redirect('/api/auth/login');
  }

  // Render page with session data
  return (
    <div>
      <h1>Analytics</h1>
      <p>User ID: {session.userId}</p>
      <p>Tenant ID: {session.tenantId}</p>
    </div>
  );
}

Nested Server Component Protection

Nested Server Components that exist within your page component tree cannot redirect. The redirect() function only works in page-level Server Components (page.tsx files), Server Actions, and API Route Handlers. Nested components must use conditional rendering instead.

// src/app/dashboard/UserProfile.tsx

import { cookies } from 'next/headers';
import { getServerComponentSession } from 'src/wristband';

export default async function UserProfile() {
  const cookieStore = await cookies();
  const session = await getServerComponentSession(cookieStore);

  if (!session.isAuthenticated) {
    return <div>Please log in to view your profile.</div>;
  }

  // Render page with session data
  return (
    <div>
      <h2>User Profile</h2>
      <p>Email: {session.email}</p>
      <p>Tenant: {session.tenantId}</p>
    </div>
  );
}


What’s Next

Next, let's learn how we can enforce authentication on Server Actions.