# How the NextAuth.js Authentication Plugin Architecture Works in prompts.chat

> Explore the modular NextAuthjs authentication plugin architecture in prompts.chat. Discover how providers are registered and dynamically assembled for a flexible auth system.

- Repository: [Fatih Kadir Akın/prompts.chat](https://github.com/f/prompts.chat)
- Tags: architecture
- Published: 2026-04-02

---

**The prompts.chat repository implements a modular NextAuth.js authentication system where each provider (Google, GitHub, Azure, etc.) is encapsulated as a pluggable AuthPlugin, registered in a global registry at runtime, and dynamically assembled into the final NextAuth configuration based on the prompts.config.ts settings.**

The open-source **prompts.chat** project demonstrates an elegant approach to authentication scalability by wrapping NextAuth.js providers in a plugin-based architecture. This design decouples provider-specific logic from the core application, allowing developers to add or remove authentication methods by simply registering plugins and updating configuration. Understanding this **NextAuth.js authentication plugin architecture** reveals how to build extensible, maintainable auth systems in Next.js applications.

## Core Components of the Plugin Architecture

### The AuthPlugin Contract

The foundation of the system is the `AuthPlugin` interface defined in [`src/lib/plugins/types.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/types.ts) (lines 7-14). This contract standardizes how authentication providers integrate with the application.

```typescript
// src/lib/plugins/types.ts
export interface AuthPlugin {
  id: string;
  name: string;
  getProvider: () => any;
}

```

Each plugin must expose a unique **id**, a display **name**, and a **getProvider()** method that returns a configured NextAuth provider object.

### Global Plugin Registry

The registry in [`src/lib/plugins/registry.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/registry.ts) (lines 4-19) maintains global `Map` instances that store active plugins. It exposes `registerAuthPlugin()` for adding providers and `getAuthPlugin()` for retrieval by ID, enabling runtime plugin resolution without hardcoded provider lists.

### Built-in Provider Implementations

Concrete implementations reside in `src/lib/plugins/auth/`. For example, [`src/lib/plugins/auth/google.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/auth/google.ts) (lines 1-12) implements the Google provider:

```typescript
// src/lib/plugins/auth/google.ts
import GoogleProvider from "next-auth/providers/google";
import type { AuthPlugin } from "../types";

export const googlePlugin: AuthPlugin = {
  id: "google",
  name: "Google",
  getProvider: () =>
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
};

```

## Configuration Flow from Registry to NextAuth

### Plugin Initialization

When the server boots, `initializePlugins()`—called from [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts) at line 10—executes `registerBuiltInAuthPlugins()`. This function iterates over each file in `src/lib/plugins/auth/` and registers them via `registerAuthPlugin()`, as implemented in [`src/lib/plugins/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/auth/index.ts) (lines 9-15).

```typescript
// src/lib/auth/index.ts (excerpt)
import { initializePlugins } from "@/lib/plugins";
initializePlugins();

```

### Dynamic Config Assembly

The `buildAuthConfig()` function in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts) (lines 22-53) orchestrates the dynamic configuration. It reads the application configuration via `getConfig()`, supporting both the modern `auth.providers: []` array and the legacy `auth.provider` string (lines 8-20).

For each configured provider ID, `getAuthPlugin(id)` looks up the plugin in the registry (lines 27-40). Missing plugins are logged and skipped; if no valid plugins are found, the system throws an error to prevent startup with broken authentication.

### Custom Prisma Adapter Integration

The architecture wraps the default NextAuth Prisma adapter in a `CustomPrismaAdapter()` (lines 30-66 in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts)). This customization adds **automatic username generation**, handles **unclaimed accounts**, and implements retry logic for username collisions before delegating to the standard adapter.

## JWT Session Management and Callbacks

The final configuration object assembled in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts) specifies `session.strategy: "jwt"` for stateless sessions and implements custom callbacks to maintain data consistency:

- **JWT Callback** (lines 54-95): Enriches the token with fresh database user data on every sign-in and subsequent request, invalidating tokens when users are deleted from the database.
- **Session Callback** (lines 96-112): Copies JWT fields into the `session.user` object delivered to the client application.

## Implementing a Custom Authentication Provider

Adding a new provider requires three steps following the established **NextAuth.js authentication plugin architecture**:

1. **Create the plugin file** at [`src/lib/plugins/auth/example.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/auth/example.ts):

```typescript
import ExampleProvider from "next-auth/providers/example";
import type { AuthPlugin } from "../types";

export const examplePlugin: AuthPlugin = {
  id: "example",
  name: "Example",
  getProvider: () =>
    ExampleProvider({
      clientId: process.env.EXAMPLE_CLIENT_ID!,
      clientSecret: process.env.EXAMPLE_CLIENT_SECRET!,
    }),
};

```

2. **Register the plugin** in [`src/lib/plugins/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/auth/index.ts):

```typescript
import { examplePlugin } from "./example";
registerAuthPlugin(examplePlugin);

```

3. **Enable in configuration** by adding `"example"` to the `auth.providers` array in [`prompts.config.ts`](https://github.com/f/prompts.chat/blob/main/prompts.config.ts):

```json
{
  "auth": {
    "providers": ["credentials", "google", "example"]
  }
}

```

## Consuming Auth Helpers in the Application

The configured NextAuth instance exports `handlers`, `signIn`, `signOut`, and `auth` from [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts) (lines 17-21):

```typescript
export const { handlers, signIn, signOut, auth } = NextAuth(authConfig);

```

Use these in client components:

```tsx
// src/app/login/page.tsx
"use client";
import { signIn } from "@/lib/auth";

export function LoginButtons() {
  return (
    <button onClick={() => signIn("google")}>
      Continue with Google
    </button>
  );
}

```

And in API routes:

```typescript
// src/app/api/auth/route.ts
import { handlers } from "@/lib/auth";

export const GET = handlers;
export const POST = handlers;

```

## Summary

- The **AuthPlugin interface** in [`src/lib/plugins/types.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/types.ts) standardizes provider integration through an `id`, `name`, and `getProvider()` method.
- A **global registry** in [`src/lib/plugins/registry.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/registry.ts) maintains runtime plugin storage with `registerAuthPlugin()` and `getAuthPlugin()` utilities.
- **Dynamic configuration** occurs in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts), where `buildAuthConfig()` assembles NextAuth settings from registered plugins and [`prompts.config.ts`](https://github.com/f/prompts.chat/blob/main/prompts.config.ts).
- The **CustomPrismaAdapter** extends the default adapter to handle username generation and collision retries.
- The system exports standard **NextAuth helpers** (`signIn`, `signOut`, `auth`, `handlers`) for consumption throughout the application.

## Frequently Asked Questions

### What is the AuthPlugin interface structure?

The `AuthPlugin` interface, defined in [`src/lib/plugins/types.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/plugins/types.ts) at lines 7-14, requires three properties: `id` (unique string identifier), `name` (display name), and `getProvider()` (a method returning a configured NextAuth provider instance).

### How does prompts.chat handle missing auth providers?

During configuration assembly in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts) (lines 27-40), the system calls `getAuthPlugin(id)` for each configured provider. If a plugin is missing, it logs a warning and continues; however, if no valid providers are found at all, it throws an error to prevent startup with broken authentication.

### Can multiple authentication providers be active simultaneously?

Yes. The architecture supports an array of providers via the `auth.providers` configuration key in [`prompts.config.ts`](https://github.com/f/prompts.chat/blob/main/prompts.config.ts). The `buildAuthConfig()` function iterates through all configured IDs and aggregates their provider objects into the final NextAuth configuration.

### Where is the session strategy configured?

The session strategy is set to `"jwt"` within the configuration object built in [`src/lib/auth/index.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/auth/index.ts). This enables stateless sessions where the JWT callback (lines 54-95) enriches tokens with fresh user data from the database on each request.