How to Add a Custom Authentication Provider to NextAuth.js in prompts.chat

You add a custom authentication provider to NextAuth.js in prompts.chat by implementing the AuthPlugin interface, registering it via registerAuthPlugin() in the auth plugin index, and enabling it in the auth.providers array inside prompts.config.ts.

The prompts.chat application employs a plugin-based architecture for authentication that wraps NextAuth.js. Rather than editing core authentication logic directly, you extend the system through the AuthPlugin interface defined in src/lib/plugins/types.ts, enabling type-safe registration of OAuth providers, credentials providers, or custom authentication methods without touching the underlying NextAuth configuration.

Understanding the Plugin Architecture

The authentication system in prompts.chat decouples provider definitions from the core application through three key components:

  • AuthPlugin interface (src/lib/plugins/types.ts#L7-L14): Defines the contract every provider must implement, requiring an id, name, and a getProvider() function that returns a NextAuth provider configuration.
  • PluginRegistry (src/lib/plugins/registry.ts#L13-L16): A global store that holds all registered auth plugins and retrieves them by their id during configuration.
  • initializePlugins() (src/lib/plugins/index.ts#L23-L31): Bootstraps the system by calling registerBuiltInAuthPlugins() and then filtering the registry against the providers array defined in prompts.config.ts to load only enabled plugins.

This architecture ensures that adding a new OAuth provider—such as Discord, Slack, or a custom SAML solution—requires only creating a new plugin file and updating the configuration registry.

Step-by-Step: Add a Custom Authentication Provider

Step 1: Create the Provider Plugin

Create a new file in src/lib/plugins/auth/ that implements the AuthPlugin interface. The getProvider() method must return a valid NextAuth provider configuration.

The following example implements a Discord provider:

// src/lib/plugins/auth/discord.ts
import Discord from "next-auth/providers/discord";
import type { AuthPlugin } from "../types";

export const discordPlugin: AuthPlugin = {
  id: "discord",
  name: "Discord",
  getProvider: () =>
    Discord({
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
      // Map Discord profile to the shape used by prompts.chat
      profile(profile) {
        return {
          id: profile.id,
          name: profile.username,
          email: profile.email,
          image: profile.avatar
            ? `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.png`
            : null,
        };
      },
    }),
};

Step 2: Register the Plugin

Import your new plugin into src/lib/plugins/auth/index.ts and register it using registerAuthPlugin():

// src/lib/plugins/auth/index.ts
import { registerAuthPlugin } from "../registry";
import { credentialsPlugin } from "./credentials";
import { googlePlugin } from "./google";
import { azurePlugin } from "./azure";
import { githubPlugin } from "./github";
import { applePlugin } from "./apple";
import { discordPlugin } from "./discord";   // <-- new import

export function registerBuiltInAuthPlugins(): void {
  registerAuthPlugin(credentialsPlugin);
  registerAuthPlugin(googlePlugin);
  registerAuthPlugin(azurePlugin);
  registerAuthPlugin(githubPlugin);
  registerAuthPlugin(applePlugin);
  registerAuthPlugin(discordPlugin);      // <-- registration
}

The registerAuthPlugin() function adds your plugin to the global registry, making it available for activation.

Step 3: Enable in Configuration

Add the provider's id to the auth.providers array in prompts.config.ts:

// prompts.config.ts
export default defineConfig({
  // …
  auth: {
    providers: ["github", "google", "apple", "discord"], // <-- enable Discord
    allowRegistration: false,
  },
  // …
});

When the application initializes, getConfiguredAuthPlugins() reads this array and instantiates only the providers listed, keeping the NextAuth configuration lean and performant.

Step 4: Use in Components (Optional)

Invoke the provider using NextAuth's signIn method with the plugin id:

"use client";

import { useSession, signIn, signOut } from "next-auth/react";

export function AuthButtons() {
  const { data: session } = useSession();

  return (
    <div className="flex gap-2">
      {session ? (
        <button onClick={() => signOut()}>Sign out</button>
      ) : (
        <button onClick={() => signIn("discord")}>Sign in with Discord</button>
      )}
    </div>
  );
}

Summary

  • Implement AuthPlugin: Create a new file in src/lib/plugins/auth/ that exports an object with id, name, and a getProvider() function returning a NextAuth provider.
  • Register globally: Import and pass your plugin to registerAuthPlugin() inside src/lib/plugins/auth/index.ts.
  • Activate via config: Add the plugin's id to the auth.providers array in prompts.config.ts.
  • Type-safe resolution: The plugin system automatically resolves enabled providers at runtime without modifying core authentication code.

Frequently Asked Questions

What interface must a custom authentication provider implement?

Your provider must implement the AuthPlugin interface defined in src/lib/plugins/types.ts. This requires three properties: a unique string id, a display name, and a getProvider() function that returns a NextAuth provider configuration object (such as an OAuth provider from next-auth/providers).

Do I need to modify core files to add a new authentication provider?

No. You only need to create a new plugin file, import it into src/lib/plugins/auth/index.ts, and register it with registerAuthPlugin(). The core authentication initialization logic in src/lib/plugins/index.ts dynamically loads enabled providers from the registry without requiring changes to its source code.

How does prompts.chat determine which providers are active?

During initialization, the getConfiguredAuthPlugins() function in src/lib/plugins/index.ts reads the auth.providers array from prompts.config.ts. It filters the global PluginRegistry to return only the plugins whose id values match the configuration array, ensuring only explicitly enabled providers are passed to NextAuth.

Can I customize how OAuth profile data maps to the user model?

Yes. When defining your plugin's getProvider() function, include a profile() callback inside the provider configuration (as shown in the Discord example). This callback receives the raw OAuth profile and should return an object matching the user shape expected by prompts.chat, typically containing id, name, email, and image fields.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →