npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kenac/nextjs-oauth2-proxy-auth

v0.1.0

Published

A comprehensive authentication library for Next.js applications using OAuth2 Proxy with full RBAC support, Keycloak integration, and federated logout

Readme

@kenac/nextjs-oauth2-proxy-auth

A comprehensive authentication library for Next.js applications using OAuth2 Proxy with full RBAC (Role-Based Access Control) support.

Features

  • 🔐 OAuth2 Proxy Integration - Extracts user data from OAuth2 Proxy headers
  • 🎭 Multiple Provider Support - Keycloak, Auth0, Okta, and generic OAuth2 providers
  • 🛡️ Full RBAC - Permission and role-based access control
  • 🚪 Federated Logout - Complete logout flow with OAuth2 provider session clearing
  • Server Components - Full support for Next.js App Router and Server Components
  • 🎣 React Hooks - Comprehensive hooks for authentication and authorization
  • 🧱 Guard Components - Client and server-side route protection
  • 🔧 Highly Configurable - Customize headers, endpoints, and behavior
  • 🧪 Development Mocking - Built-in mock user for local development

Installation

npm install @kenac/nextjs-oauth2-proxy-auth
# or
pnpm add @kenac/nextjs-oauth2-proxy-auth
# or
yarn add @kenac/nextjs-oauth2-proxy-auth

Quick Start (CLI - Recommended)

The fastest way to get started is using our CLI which sets up everything automatically:

npx @kenac/nextjs-oauth2-proxy-auth init

This will:

  • ✅ Create lib/auth.config.ts with your provider configuration
  • ✅ Create app/api/auth/[...auth]/route.ts (handles all auth API routes)
  • ✅ Create app/logout-helper/page.tsx for federated logout
  • ✅ Create app/providers.tsx with the UserProvider wrapper
  • ✅ Create .env.example with required environment variables

After running the CLI, just:

  1. Copy environment variables: cp .env.example .env.local and fill in your values
  2. Update app/layout.tsx to wrap children with the Providers component
  3. Configure OAuth2 Proxy to skip auth for logout helper: --skip-auth-routes=^/logout-helper

That's it! Start using authentication in your components:

import { useUser, AuthGuard, PermissionGuard } from "@kenac/nextjs-oauth2-proxy-auth";

CLI Options

# Interactive setup (prompts for provider)
npx @kenac/nextjs-oauth2-proxy-auth init

# Specify provider directly
npx @kenac/nextjs-oauth2-proxy-auth init --provider keycloak
npx @kenac/nextjs-oauth2-proxy-auth init --provider auth0
npx @kenac/nextjs-oauth2-proxy-auth init --provider okta
npx @kenac/nextjs-oauth2-proxy-auth init --provider generic

Manual Setup

If you prefer to set up manually, follow these steps:

1. Create Configuration

// lib/auth-config.ts
import { createAuthConfig } from "@kenac/nextjs-oauth2-proxy-auth";

export const authConfig = createAuthConfig({
  provider: {
    type: "keycloak",
    domain: process.env.KEYCLOAK_DOMAIN!,
    realm: process.env.KEYCLOAK_REALM!,
  },
  development: {
    enableMocking: process.env.NODE_ENV === "development",
    mockUser: {
      email: "[email protected]",
      name: "Development User",
      roles: ["admin", "user"],
    },
  },
});

2. Set Up API Routes

Option A: Single catch-all route (recommended)

// app/api/auth/[...auth]/route.ts
import { createAuthRouteHandler } from "@kenac/nextjs-oauth2-proxy-auth/server";
import { authConfig } from "@/lib/auth-config";

const handler = createAuthRouteHandler(authConfig);

export const GET = handler;
export const dynamic = "force-dynamic";

This single file handles /api/auth/user, /api/auth/id-token, and /api/auth/health.

Option B: Individual routes

// app/api/auth/user/route.ts
import { createUserHandler } from "@kenac/nextjs-oauth2-proxy-auth/server";
import { authConfig } from "@/lib/auth-config";

export const GET = createUserHandler({
  headers: authConfig.headers,
  development: authConfig.development,
});
// app/api/auth/id-token/route.ts
import { createIdTokenHandler } from "@kenac/nextjs-oauth2-proxy-auth/server";

export const GET = createIdTokenHandler();

3. Create Logout Helper Page

// app/logout-helper/page.tsx
import { LogoutHelperPage } from "@kenac/nextjs-oauth2-proxy-auth";

export default function Page() {
  return <LogoutHelperPage />;
}

⚠️ Important: Configure OAuth2 Proxy to skip auth for this page: --skip-auth-routes=^/logout-helper

4. Wrap Your App with UserProvider

// app/providers.tsx
"use client";

import { UserProvider } from "@kenac/nextjs-oauth2-proxy-auth";
import { authConfig } from "@/lib/auth-config";

export function Providers({ children }: { children: React.ReactNode }) {
  return <UserProvider config={authConfig}>{children}</UserProvider>;
}
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

5. Use in Components

"use client";

import { useUser, useAuth, PermissionGuard } from "@kenac/nextjs-oauth2-proxy-auth";

export function Dashboard() {
  const { user, loading, isAuthenticated } = useUser();
  const { logout } = useAuth();

  if (loading) return <div>Loading...</div>;
  if (!isAuthenticated) return <div>Please log in</div>;

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      
      <PermissionGuard permission="dashboard:admin" fallback={<p>Admin only</p>}>
        <AdminPanel />
      </PermissionGuard>
      
      <button onClick={() => logout()}>Logout</button>
    </div>
  );
}

Configuration

Full Configuration Example

import { createAuthConfig } from "@kenac/nextjs-oauth2-proxy-auth";

const authConfig = createAuthConfig({
  // OAuth2 Provider Configuration
  provider: {
    type: "keycloak", // "keycloak" | "auth0" | "okta" | "generic"
    domain: "https://auth.example.com",
    realm: "my-realm", // For Keycloak
    clientId: "my-client", // Optional
  },

  // Header Configuration (customize if OAuth2 Proxy uses different headers)
  headers: {
    userIdHeaders: ["x-forwarded-user", "x-user", "x-auth-request-user"],
    emailHeaders: ["x-forwarded-email", "x-email", "x-auth-request-email"],
    usernameHeaders: ["x-forwarded-preferred-username"],
    groupsHeaders: ["x-forwarded-groups", "x-groups"],
  },

  // RBAC Configuration
  rbac: {
    enabled: true,
    fetchPermissionsOnLogin: true,
    permissionsCacheDuration: 5 * 60 * 1000, // 5 minutes
    // Custom permission fetcher
    fetchPermissions: async (userId) => {
      const res = await fetch(`/api/permissions?userId=${userId}`);
      const data = await res.json();
      return data.permissions;
    },
  },

  // Logout Configuration
  logout: {
    oauth2ProxyLogout: "/oauth2/sign_out",
    logoutHelperPath: "/logout-helper",
    postLogoutRedirect: "/",
    includeIdTokenHint: true,
    localStorageKeys: ["user", "auth_token"],
  },

  // Development Configuration
  development: {
    enableMocking: process.env.NODE_ENV === "development",
    enableLogging: true,
    mockUser: {
      id: "dev-user",
      email: "[email protected]",
      name: "Dev User",
      roles: ["admin"],
      permissions: ["dashboard:view", "users:manage"],
    },
  },

  // Endpoints Configuration
  endpoints: {
    userInfo: "/api/auth/user",
    idToken: "/api/auth/id-token",
    login: "/oauth2/start",
    logout: "/oauth2/sign_out",
  },
});

Hooks

useUser()

Access the current user and authentication state.

const { user, loading, error, isAuthenticated, refetch } = useUser();

useAuth()

Access authentication actions.

const { login, logout, refreshSession, validateSession, clearLocalSession } = useAuth();

// Login with redirect
login("/dashboard");

// Logout
await logout();

// Logout without provider logout (just clear local session)
await logout({ skipProviderLogout: true });

usePermissions()

Check user permissions and roles.

const {
  hasPermission,
  hasAnyPermission,
  hasAllPermissions,
  hasRole,
  hasAnyRole,
  isAdmin,
  isManager,
  permissions,
  roles,
} = usePermissions();

// Check single permission
if (hasPermission("users:delete")) {
  // ...
}

// Check multiple permissions
if (hasAnyPermission(["users:edit", "users:delete"])) {
  // ...
}

useUserDisplay()

Get formatted user display information.

const { displayName, displayEmail, initials, avatar, primaryRole } = useUserDisplay();

Components

<AuthGuard>

Protect content based on authentication.

<AuthGuard fallback={<LoginPrompt />}>
  <ProtectedContent />
</AuthGuard>

<AuthGuard when="unauthenticated">
  <LoginButton />
</AuthGuard>

<AuthGuard roles={["admin", "manager"]} fallback={<AccessDenied />}>
  <AdminContent />
</AuthGuard>

<PermissionGuard>

Protect content based on permissions.

<PermissionGuard permission="users:delete" fallback={<NoAccess />}>
  <DeleteButton />
</PermissionGuard>

<PermissionGuard permissions={["users:edit", "users:view"]} requireAll>
  <UserEditor />
</PermissionGuard>

<RoleGuard>

Protect content based on roles.

<RoleGuard role="admin">
  <AdminPanel />
</RoleGuard>

<RoleGuard roles={["admin", "manager"]}>
  <ManagementTools />
</RoleGuard>

withAuth() HOC

Protect entire components with a higher-order component.

const ProtectedPage = withAuth(MyPage, {
  requiredRoles: ["admin"],
  redirectUrl: "/login",
  fallback: AccessDeniedComponent,
});

Server-Side Usage

Server Actions

// lib/actions/get-user.ts
"use server";

import { createServerUserAction } from "@kenac/nextjs-oauth2-proxy-auth/server";
import { authConfig } from "@/lib/auth-config";

export const getServerUser = createServerUserAction({
  headers: authConfig.headers,
  development: authConfig.development,
  enrichUserWithProfile: async (user) => {
    // Fetch additional user data from your database
    const profile = await db.profiles.findByEmail(user.email);
    return { ...user, profileId: profile?.id };
  },
  fetchRBAC: async (userId) => {
    // Fetch roles and permissions from your backend
    const rbac = await db.rbac.getForUser(userId);
    return {
      roles: rbac.roles,
      permissions: rbac.permissions,
    };
  },
});

Server Components (Page Guards)

// lib/guards.ts
import { createServerGuards } from "@kenac/nextjs-oauth2-proxy-auth/server";
import { getServerUser } from "@/lib/actions/get-user";

export const { PermissionGuard, RoleGuard, AdminGuard, AuthGuard } =
  createServerGuards(getServerUser);
// app/admin/page.tsx
import { AdminGuard } from "@/lib/guards";

export default async function AdminPage() {
  return (
    <AdminGuard redirectTo="/unauthorized">
      <h1>Admin Dashboard</h1>
    </AdminGuard>
  );
}

OAuth2 Proxy Configuration

Ensure your OAuth2 Proxy is configured to pass the required headers:

# oauth2-proxy.cfg or environment variables
pass_user_headers = true
pass_authorization_header = true  # Required for logout with id_token_hint
set_xauthrequest = true

# Skip auth for logout helper page
skip_auth_routes = ["/logout-helper"]

Logout Flow

The library implements a complete federated logout flow:

  1. Frontend calls logout() → fetches ID token, clears local storage
  2. Redirect to OAuth2 Provider (e.g., Keycloak) with id_token_hint
  3. Provider clears session → redirects to /logout-helper
  4. Logout helper redirects to /oauth2/sign_out
  5. OAuth2 Proxy clears its session → redirects to home

TypeScript

The library is written in TypeScript and includes full type definitions.

import type {
  User,
  UserResponse,
  OAuth2ProxyAuthConfig,
  PermissionCheck,
} from "@kenac/nextjs-oauth2-proxy-auth";

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a pull request.