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 requireMiddlewareAuth = 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:

  1. The middleware validates their session.
    • If authenticated, the page loads normally.
    • If not authenticated, they're redirected to the Login Endpoint.
  2. If an access token has expired, the middleware automatically refreshes it.

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.

Configure Server Component Session Helper

First, add a read-only session helper to your src/wristband.ts file for accessing 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 session data for authentication checks and data display.

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 requireMiddlewareAuth = wristbandAuth.createMiddlewareAuth({
  authStrategies: ['SESSION'],
  sessionConfig: {
    sessionOptions,
    sessionEndpoint: '/api/auth/session'
  },
  protectedPages: ['/', '/dashboard', '/settings(.*)'],
});

/*
 * NEW: Configure read-only session helper for 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, we'll add auth and session helpers for authenticating your Server Actions since they bypass auth middleware.