payload-authjs

repository·main·Indexed 41 days ago

https://github.com/crawlercode/payload-authjs

A Payload CMS 3 plugin that integrates Auth.js 5 (beta) to provide authentication using Auth.js providers while storing user data in the Payload CMS database. It features a custom database adapter and auth strategy, support for multiple authentication collections, and compatibility with Payload's Local JWT Strategy.

Tokens
14.2K
Snippets
45
Records
54
Agent score
52%

What's inside payload-authjs

  1. How payload-authjs works

    main

    The payload-authjs plugin integrates Auth.js 5 (beta) with Payload CMS 3 by:

    1. Automatically creating a users collection in Payload CMS.
    2. Using a custom Auth.js Database Adapter to store users within the Payload CMS database.
    3. Using a custom Payload CMS Auth Strategy to retrieve sessions from Auth.js.

    This allows you to sign in via Auth.js providers and be automatically authenticated within Payload CMS.

  2. Use multiple authentication collections with payload-authjs

    main

    To support different user types (e.g., customers and admins) in a single application, you can register multiple instances of the payload-authjs plugin within your Payload CMS configuration. Each plugin instance is tied to a specific collection and maintains its own Auth.js configuration, handlers, and authentication workflows.

    In a typical setup:

    • Each collection (e.g., customers, admins) has a dedicated Auth.js configuration file.
    • Each collection has its own set of Auth.js handlers.
    • The payload.config.ts file includes a separate payload-authjs plugin entry for every authentication collection you wish to support.
    // src/payload.config.ts
    // Example of registering multiple plugins
    export default buildConfig({
      // ... other config
      plugins: [
        payloadAuthjs({ 
          collection: customers, 
          authConfig: customersAuthConfig 
        }),
        payloadAuthjs({ 
          collection: admins, 
          authConfig: adminsAuthConfig 
        }),
      ],
    });
  3. Setup Auth.js for payload-authjs

    main

    Before adding the plugin to Payload, you must configure Auth.js. This involves three main steps:

    1. Create Auth.js configuration

    Define your providers in an auth.config.ts file.

    2. Create the Auth.js instance

    Use getAuthjsInstance from payload-authjs. Note that you must initialize the Payload instance first.

    3. Add the route handler

    Expose the Auth.js handlers in your Next.js app router at /app/api/auth/[...nextauth]/route.ts.

    // 1. auth.config.ts
    import type { NextAuthConfig } from "next-auth";
    import github from "next-auth/providers/github";
    
    export const authConfig: NextAuthConfig = {
      providers: [github],
    };
    
    // 2. auth.ts
    import payloadConfig from "@payload-config";
    import { getPayload } from "payload";
    import { getAuthjsInstance } from "payload-authjs";
    
    const payload = await getPayload({ config: payloadConfig });
    export const { handlers, signIn, signOut, auth } = getAuthjsInstance(payload);
    
    // 3. app/api/auth/[...nextauth]/route.ts
    import { handlers } from "@/auth";
    export const { GET, POST } = handlers;
  4. Get the payload session on the Client side

    main

    To use the usePayloadSession hook in client-side components, you must first wrap your application (e.g., in layout.tsx) with the PayloadSessionProvider.

    // 1. Wrap your app in layout.tsx
    import { PayloadSessionProvider } from "payload-authjs/client";
    import { getPayloadSession } from "payload-authjs";
    
    const Layout: React.FC<{ children: React.ReactNode }> = async ({ children }) => {
      return (
        <html lang="en">
          <body>
            <PayloadSessionProvider session={await getPayloadSession()}>
              {children}
            </PayloadSessionProvider>
          </body>
        </html>
      );
    };
    
    // 2. Use the hook in a client component
    "use client";
    import { usePayloadSession } from "payload-authjs/client";
    
    export const ClientComponentExample = () => {
      const { session } = usePayloadSession();
      return <pre>{JSON.stringify(session?.user)}</pre>;
    };
  5. Set up the payload-authjs basic example

    main

    To run the basic example project, follow these steps:

    1. Configure Environment Variables: Rename the .env.example file to .env and update the values with your specific secrets.
    2. Install Dependencies: Use pnpm to install the required packages.
    3. Start Development Server: Run the development script to launch the application.
    cp .env.example .env
    pnpm install
    pnpm run dev
  6. Add virtual fields to the users collection (JWT strategy only)

    main

    If you are using the Auth.js jwt session strategy, you can add data to the user object without storing it in the database by using Payload virtual fields.

    To make this work, you must:

    1. Define the field in your collection with virtual: true.
    2. Extend the Auth.js jwt callback to include the data in the token.
    3. Extend the Auth.js session callback to forward the data from the token to the session.

    The plugin will then automatically extract these virtual fields from the Auth.js session and attach them to the Payload user object.

    // 1. Define virtual field in Payload
    const Users: CollectionConfig = {
      slug: "users",
      fields: [
        {
          name: "roles",
          type: "json",
          virtual: true,
          admin: { hidden: true },
        },
      ],
    };
    
    // 2. Extend Auth.js JWT and Session callbacks
    const authConfig: NextAuthConfig = {
      callbacks: {
        jwt: ({ token, trigger }) => {
          if (trigger === "signIn" || trigger === "signUp") {
            token.roles = ["example-role"];
          }
          return token;
        },
        session: ({ session, token }) => {
          if (token) {
            session.user.roles = token.roles;
          }
          return session;
        },
      },
    };
  7. Configure Auth.js proxy/middleware

    main

    To keep the session alive by updating the expiry on each call, add a proxy or middleware. The implementation depends on your Next.js version.

    Next.js 16 or above

    Use proxy.ts with the nodejs runtime.

    Next.js 15

    Use middleware.ts. Note that you cannot use the @/auth middleware directly; you must create a separate Auth.js instance that is edge-compatible using your authConfig.

    // Next.js 16+ (proxy.ts)
    export { auth as proxy } from "./auth";
    
    export const config = {
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico|admin).*)"],
    };
    
    // Next.js 15 (middleware.ts)
    import NextAuth from "next-auth";
    import { authConfig } from "./auth.config";
    
    export const { auth: middleware } = NextAuth(authConfig);
    
    export const config = {
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico|admin).*)"],
    };
  8. Customize the users collection

    main

    The plugin automatically creates a collection with the slug users. To customize it (e.g., adding fields or changing labels), create your own collection with the exact same slug users. The fields you define will be merged with the default fields provided by the plugin.

    // users.ts
    const Users: CollectionConfig = {
      slug: "users",
      fields: [
        {
          name: "id",
          type: "text",
          label: "Identifier",
          admin: { hidden: true },
        },
        {
          name: "accounts",
          type: "array",
          fields: [
            {
              name: "provider",
              type: "text",
              label: "Account Provider",
            },
          ],
        },
      ],
    };
  9. Configure payload-authjs with Payload Local JWT Strategy

    main

    To use payload-authjs alongside Payload's Local JWT Strategy, you must enable the strategy within the plugin configuration. This allows Auth.js to leverage Payload's built-in JWT authentication mechanism.

    In your Payload CMS configuration file (e.g., src/payload.config.ts), add the payload-authjs plugin and set the enableLocalStrategy option to true.

    // In src/payload.config.ts
    import { payloadAuthjs } from 'payload-authjs';
    
    export default buildConfig({
      // ... other config
      plugins: [
        payloadAuthjs({
          enableLocalStrategy: true,
        }),
      ],
    });
  10. Add database fields to the users collection

    main

    To store additional user data permanently in the database, add a new field to your Payload users collection and extend your Auth.js provider's profile method to map the incoming provider data to that field.

    Note: Auth.js does not automatically update the user record after the initial sign-in. To sync data on every sign-in, use the signIn event instead.

    // 1. Add field to Payload collection
    const Users: CollectionConfig = {
      slug: "users",
      fields: [
        {
          name: "locale",
          type: "text",
        },
      ],
    };
    
    // 2. Extend Auth.js provider profile mapping
    const authConfig: NextAuthConfig = {
      providers: [
        keycloak({
          profile(profile) {
            return {
              id: profile.sub,
              name: profile.name,
              email: profile.email,
              image: profile.picture,
              locale: profile.locale, // <-- Map custom field
            };
          },
        }),
      ],
    };