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

@freshheads/react-auth

v0.0.5

Published

A type-safe React authentication library for Next.js and react applications with built-in role-based access control (RBAC) and JWT token management. Based on our own authentication backend.

Readme

@freshheads/react-auth

A type-safe React authentication library for Next.js and react applications with built-in role-based access control (RBAC) and JWT token management. Based on our own authentication backend.

Features

  • 🔐 Authentication Management - Built-in login/logout functionality with JWT token handling
  • Auth Levels - Supports authLevel states on session (partial, setup_required, full)
  • 🔒 Optional MFA Redirect Flow - Redirect partial sessions to an MFA route until challenge is completed
  • 🛡️ Role-Based Access Control (RBAC) - Define route access based on user roles
  • 🎯 Type-Safe - Full TypeScript support with generic types for routes and roles
  • 🚀 Next.js Middleware Integration - Server-side route protection
  • 🪝 React Hooks - Easy-to-use hooks for authentication state and utilities
  • 🍪 Cookie Management - Automatic JWT cookie handling

Installation

npm install @freshheads/react-auth

Peer Dependencies

This package requires the following peer dependencies:

npm install react react-dom next typescript

Configuration Options

ReactAuthConfig

| Property | Type | Description | |----------|------|-------------| | rbacRoutes | RbacRoutes | Maps roles to their accessible routes and landing pages | | defaultRoutes | { login: ValidRoute, landing: ValidRoute } | Default login and landing routes | | openRoutes | ValidRoute[] | Routes accessible to everyone (authenticated or not) | | unauthenticatedRoutes | ValidRoute[] | Routes only accessible when NOT authenticated | | hiddenRoutePrefix | string | Prefix to ignore for route matching (e.g., {/:locale}) | | mfa | { landingRoute?: ValidRoute, allowedRoutes?: ValidRoute[] } or true | Optional MFA settings for partial sessions, true enables with default settings {landingRoute: '/mfa'} | | fetchMutator | FetchMutator | Optional fetch wrapper used by adapters/session/hooks | | session | SessionStrategy | Session strategy for getSession | | adapters | AuthAdapter[] | Auth adapters for login; logout is handled via the session strategy |

Session Auth Levels

Session can contain an optional authLevel value:

  • partial: primary login succeeded, but MFA steps are still required
  • setup_required: MFA enrollment is required before full authentication
  • full: fully authenticated

This package currently enforces route behavior for partial when mfa config is present.

RbacRoutes

type RbacRoutes<ValidRoute, AuthorizedRoles extends string[]> = {
  [key in AuthorizedRoles[number]]: {
    routes: ValidRoute[];
    landing?: ValidRoute;
  };
};

How It Works

  1. Middleware Protection: The createMiddleware handler checks each request and determines whether the user can access the route or must be redirected.

  2. Session Strategy: You provide a session strategy (cookie, token, or custom) to define how sessions are fetched and updated.

  3. Role-Based Access: Routes are protected based on the roles defined in rbacRoutes. Users can only access routes that match their assigned roles.

TypeScript Support

This library is built with TypeScript and provides full type safety:

// Define your specific types
type MyRoutes = '/home' | '/dashboard' | '/admin';
type MyRoles = 'user' | 'admin' | 'moderator';

// Get full type checking and autocomplete
const config: ReactAuthConfig<MyRoutes, MyRoles[]> = {
  // TypeScript will validate all routes and roles
};

Generic Action Return Types

You can type adapter action results by passing generics to createPasswordAdapter.

import { createPasswordAdapter } from '@freshheads/react-auth/adapters';

type LoginResponse = {
    token: string;
    userId: string;
};

type ResetRequestResponse = {
    queued: boolean;
};

type ResetResponse = {
    success: true;
};

type ChangeResponse = {
    success: true;
};

const passwordAdapter = createPasswordAdapter<
    LoginResponse,
    ResetRequestResponse,
    ResetResponse,
    ChangeResponse
>({
    login: { apiPath: '/api/auth/login' },
    resetRequest: { apiPath: '/api/auth/password/reset-request' },
    reset: { apiPath: '/api/auth/password/reset' },
    change: { apiPath: '/api/auth/password/change' },
});

async function example() {
    const loginResult = await passwordAdapter.actions.login({
        email: '[email protected]', password: 'secret',
    });

    // Fully typed as LoginResponse
    console.log(loginResult.token);
}

If an optional endpoint is not configured (resetRequest, reset, or change), the return type includes a typed error object:

{ status: 500, error: '...-not-configured' }

Usage

1. Define Your Configuration

Create a configuration object that defines your routes, roles, session strategy, and auth adapters:

// src/auth/react-auth.config.ts
import { fetchMutator } from '@/api/fetch';
import { getAuthMeGetUrl, getLogoutPostUrl } from '@/api/generated/auth/auth';
import { MeJsonldRolesItem } from '@/api/generated/model/meJsonldRolesItem';
import { getPasswordLoginPostUrl } from '@/api/generated/password/password';
import { BaseUser, ReactAuthConfig, createAuth } from '@freshheads/react-auth';
import { createPasswordAdapter } from '@freshheads/react-auth/adapters';
import { createCookieSessionStrategy } from '@freshheads/react-auth/session';
import { Route } from 'next';

export const authorizedRoles = [
    MeJsonldRolesItem.ROLE_MANAGER,
    MeJsonldRolesItem.ROLE_COMPANY_ADMIN,
    MeJsonldRolesItem.ROLE_USER,
];

type AppRole = (typeof authorizedRoles)[number];

type AppUser = BaseUser<AppRole> & {
    language: string; // extend this if there is extra info in your session user.
};

const session = createCookieSessionStrategy<AppUser>({
    logoutApiPath: getLogoutPostUrl(),
    // Optional: used for client-side refresh when no cookie is available.
    // Responses like 401/403 are treated as unauthenticated and do not throw.
    sessionApiPath: getAuthMeGetUrl(),
});

const passwordAdapter = createPasswordAdapter({
    login: {
        apiPath: getPasswordLoginPostUrl(),
    },
});

const authConfig: ReactAuthConfig<Route, AppRole[], typeof session> = {
    rbacRoutes: { // possibly keep these in a separate file to keep the config clean
        ROLE_COMPANY_ADMIN: {
            routes: [
                '/componentCollection',
                '/training-plans/:id/edit',
                '/training-plans/new',
                '/tags',
                '/all-trainings',
                '/trainings/:id/preview',
            ],
        },
        ROLE_MANAGER: {
            routes: [
                '/groups',
                '/groups/:id',
                '/groups/:id/edit',
                '/groups/new',
                '/employees',
                '/employees/:id',
                '/employees/:id/edit',
                '/employees/new',
                '/training-plans',
                '/training-plans/:id',
                '/training-plans/:id/training/:id',
            ],
        },
        ROLE_USER: {
            routes: [
                '/',
                '/trainings/:id',
                '/trainings/:id/results/:attemptId',
                '/results',
            ],
        },
    },
    defaultRoutes: {
        login: '/login',
        landing: '/',
    },
    openRoutes: [],
    unauthenticatedRoutes: ['/login', '/activate-account/:token'],
    hiddenRoutePrefix: '{/:locale}', // optional if you are using next-intl
    mfa: {
        landingRoute: '/mfa',
        allowedRoutes: ['/mfa/help'],
    },
    session: session,
    fetchMutator: fetchMutator, // optional
    adapters: [passwordAdapter],
};

export const auth = createAuth(authConfig);

2. Set Up Next.js Middleware

Create or update your middleware.ts or proxy.ts file:

// src/proxy.ts
import { createMiddleware } from '@freshheads/react-auth/next';
import { auth } from './auth';

export default createMiddleware(auth);

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

An example that passes a possible response to another middleware

// src/proxy.ts
import { auth } from '@/auth/react-auth.config';
import { routing } from '@/i18n/routing';
import { createMiddleware as createAuthMiddleware } from '@freshheads/react-auth/next';
import createMiddleware from 'next-intl/middleware';
import { NextRequest } from 'next/server';

export async function proxy(request: NextRequest) {
    const handleI18nRouting = createMiddleware(routing);
    const I18nResponse = handleI18nRouting(request);

    const middleware = createAuthMiddleware(auth);
    const response = await middleware(request, I18nResponse);

    return response;
}
export const config = {
    matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)',
};

3. Wrap Your App with AuthProvider

In your root layout or app component:

// src/app/layout.tsx
import Providers from '@/app/[locale]/providers';
import { auth } from '@/auth/react-auth.config';

export default async function RootLayout({
    children,
}: LayoutProps<'/[locale]'>) {
    const session = await auth.server.getSession();

    return (
        <html>
            <body>
                <Providers session={session}>{children}</Providers>
            </body>
        </html>
    );
}
// src/app/providers.tsx
import { AuthProvider } from '@freshheads/react-auth/react';
import { auth } from '@/auth/react-auth.config';

export default function Providers({
    children,
    session,
}: {
    children: ReactNode;
    session: Session;
}) {


  return (
    <AuthProvider initialSession={session} auth={auth}>
        {children}
    </AuthProvider>
  );
}

5. Use Authentication Hooks

Check User Authentication and Roles

'use client';

import { useSession, useUser } from '@freshheads/react-auth/react';

export default function Dashboard() {
    const { isAuthenticated, authLevel } = useSession();
  const { user } = useUser();

  return (
    <div>
      <h1>Dashboard</h1>
      <div>Authenticated: {String(isAuthenticated)}</div>
            <div>Auth level: {authLevel ?? 'none'}</div>
      <div>User: {user?.id ?? 'none'}</div>
      <pre>{JSON.stringify(user)}</pre>
    </div>
  );
}

#### Username/password login

```typescript
'use client';

import { useSession, usePasswordLogin } from '@freshheads/react-auth/react';

export default function LoginForm() {
    const { login } = usePasswordLogin();
    const { refreshSession } = useSession();
    const router = useRouter();

    const handleSubmit = async (e) => {
        e.preventDefault();
            const result = await login({
            credentials: {
                email: '[email protected]',
                password: 'pass',
            },
        });

        await refreshSession();
        router.refresh();
    };

    return (
    <div>
        <form onSubmit={handleSubmit}>{/* form fields */}</form>
    </div>
    );
}

logout

'use client';

import { useSession } from '@freshheads/react-auth/react';
import { useRouter } from '@/i18n/navigation';
import { useQueryClient } from '@tanstack/react-query';
import { FC } from 'react';

const LogoutButton: FC = () => {
    const { logout } = useSession();
    const router = useRouter();
    const queryClient = useQueryClient();
    const handleLogout = async () => {
        try {
            await logout();
            router.refresh();
            queryClient.clear();
        } catch (error) {
            console.error(error);
        }
    };

    return <button onClick={handleLogout}>Uitloggen</button>;
};

export default LogoutButton;

Middleware And Server Header Handling

  • createMiddleware(auth) passes request.headers to auth.resolveRoute(...).
  • auth.resolveRoute(...) uses the passed headers for session resolution, which is safe in middleware contexts.
  • Next.js server-only APIs are only used by auth.server.* helpers.

6. Server-Side Helpers

Require a Session

import { auth } from './auth';

export async function GET() {
  const result = await auth.server.requireSession();

  if (!result.allowed) {
    return Response.redirect(new URL(result.redirect, 'https://example.com'));
  }

  return Response.json({ user: result.session.user });
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.