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:
AuthPlugininterface (src/lib/plugins/types.ts#L7-L14): Defines the contract every provider must implement, requiring anid,name, and agetProvider()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 theiridduring configuration.initializePlugins()(src/lib/plugins/index.ts#L23-L31): Bootstraps the system by callingregisterBuiltInAuthPlugins()and then filtering the registry against theprovidersarray defined inprompts.config.tsto 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 insrc/lib/plugins/auth/that exports an object withid,name, and agetProvider()function returning a NextAuth provider. - Register globally: Import and pass your plugin to
registerAuthPlugin()insidesrc/lib/plugins/auth/index.ts. - Activate via config: Add the plugin's
idto theauth.providersarray inprompts.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →