Better Auth

Add secure authentication to your MCP server using Better Auth and PostgreSQL

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

Overview

The Better Auth plugin provides comprehensive authentication for your xmcp server using Better Auth, supporting email/password authentication, OAuth providers, and session management.

Installation

Install the Better Auth plugin and PostgreSQL dependencies:

pnpm i @xmcp-dev/better-auth pg
pnpm i -D @types/pg

Database Setup

Better Auth requires a PostgreSQL database with specific tables for user management, sessions, and OAuth applications.

Run the following SQL script to create the necessary tables:

Code
-- User table for storing user information
CREATE TABLE "user" (
  "id" text NOT NULL PRIMARY KEY,
  "name" text NOT NULL,
  "email" text NOT NULL UNIQUE,
  "emailVerified" boolean NOT NULL,
  "image" text,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL
);

-- Session table for managing user sessions
CREATE TABLE "session" (
  "id" text NOT NULL PRIMARY KEY,
  "expiresAt" timestamp NOT NULL,
  "token" text NOT NULL UNIQUE,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL,
  "ipAddress" text,
  "userAgent" text,
  "userId" text NOT NULL REFERENCES "user" ("id")
);

-- Account table for OAuth and local authentication
CREATE TABLE "account" (
  "id" text NOT NULL PRIMARY KEY,
  "accountId" text NOT NULL,
  "providerId" text NOT NULL,
  "userId" text NOT NULL REFERENCES "user" ("id"),
  "accessToken" text,
  "refreshToken" text,
  "idToken" text,
  "accessTokenExpiresAt" timestamp,
  "refreshTokenExpiresAt" timestamp,
  "scope" text,
  "password" text,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL
);

-- Verification table for email verification and password resets
CREATE TABLE "verification" (
  "id" text NOT NULL PRIMARY KEY,
  "identifier" text NOT NULL,
  "value" text NOT NULL,
  "expiresAt" timestamp NOT NULL,
  "createdAt" timestamp,
  "updatedAt" timestamp
);

-- OAuth application table for OAuth provider functionality
CREATE TABLE "oauthApplication" (
  "id" text NOT NULL PRIMARY KEY,
  "name" text NOT NULL,
  "icon" text,
  "metadata" text,
  "clientId" text NOT NULL UNIQUE,
  "clientSecret" text,
  "redirectURLs" text NOT NULL,
  "type" text NOT NULL,
  "disabled" boolean,
  "userId" text,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL
);

-- OAuth access token table
CREATE TABLE "oauthAccessToken" (
  "id" text NOT NULL PRIMARY KEY,
  "accessToken" text NOT NULL UNIQUE,
  "refreshToken" text NOT NULL UNIQUE,
  "accessTokenExpiresAt" timestamp NOT NULL,
  "refreshTokenExpiresAt" timestamp NOT NULL,
  "clientId" text NOT NULL,
  "userId" text,
  "scopes" text NOT NULL,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL
);

-- OAuth consent table for managing user consent
CREATE TABLE "oauthConsent" (
  "id" text NOT NULL PRIMARY KEY,
  "clientId" text NOT NULL,
  "userId" text NOT NULL,
  "scopes" text NOT NULL,
  "createdAt" timestamp NOT NULL,
  "updatedAt" timestamp NOT NULL,
  "consentGiven" boolean NOT NULL
);

Environment Variables

Configure the following environment variables in your .env file:

Code
# Database connection string
DATABASE_URL=postgresql://<username>:<password>@<host>:<port>/<database>

# Better Auth configuration
BETTER_AUTH_SECRET=<your-secret-key>
BETTER_AUTH_BASE_URL=<your-app-base-url>

# Optional: OAuth provider credentials
GOOGLE_CLIENT_ID=<your-google-client-id>
GOOGLE_CLIENT_SECRET=<your-google-client-secret>

Configuration

Create a middleware.ts file in your xmcp app root directory:

src/middleware.ts
import { betterAuthProvider } from "@xmcp-dev/better-auth";
import { Pool } from "pg";

export default betterAuthProvider({
  database: new Pool({
    connectionString: process.env.DATABASE_URL,
  }),
  baseURL: process.env.BETTER_AUTH_BASE_URL || "http://127.0.0.1:3001",
  secret: process.env.BETTER_AUTH_SECRET || "super-secret-key",
  providers: {
    emailAndPassword: {
      enabled: true,
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID || "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
    },
  },
});

Configuration Options

  • database - PostgreSQL Pool instance for database connections
  • baseURL - Base URL of your app for generating OAuth callback URLs
  • secret - Secret key for signing JWT tokens
  • providers - Authentication provider configuration

Authentication Providers

Email and Password

Enable email/password authentication:

Code
export default betterAuthProvider({
  // ... other config
  providers: {
    emailAndPassword: {
      enabled: true,
    },
  },
});

Google OAuth

To enable Google OAuth:

  1. Visit the Google Cloud Console
  2. Create or select a project
  3. Enable the Google+ API
  4. Create OAuth 2.0 credentials
  5. Set authorized redirect URI:
    • Development: http://localhost:3001/auth/callback/google
    • Production: https://yourdomain.com/auth/callback/google
Code
export default betterAuthProvider({
  // ... other config
  providers: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    },
  },
});

Multiple Providers

You can enable multiple authentication methods simultaneously:

Code
export default betterAuthProvider({
  // ... other config
  providers: {
    emailAndPassword: {
      enabled: true,
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    },
  },
});

Usage in Tools

Access the authenticated user session in your xmcp tools using getBetterAuthSession:

src/tools/get-user-profile.ts
import { getBetterAuthSession } from "@xmcp-dev/better-auth";

export default async function getUserProfile() {
  const session = await getBetterAuthSession();

  return `Hello! Your user id is ${session.userId}`;
}

Login Page

The authentication UI is automatically generated and available at:

Code
http://host:port/auth/sign-in

This page handles both sign-in and sign-up functionality based on your provider configuration.

Next Steps

After authentication is configured, users will be prompted to authenticate when establishing a connection to your MCP server.