How the @ferdiunal/refine-shadcn-auth Package Integrates with better-auth

The @ferdiunal/refine-shadcn-auth package bridges Refine and better-auth through a three-layer architecture that re-exports the raw better-auth client, wraps HTTP endpoints in an AuthClient class, and implements Refine's AuthProvider contract to map authentication lifecycle methods to better-auth API calls.

The @ferdiunal/refine-shadcn-auth package provides a seamless integration between the Refine React framework and the better-auth authentication library. This integration allows developers to use better-auth as a drop-in authentication backend while maintaining full access to the underlying library's capabilities. According to the ferdiunal/refine-shadcn source code, the package achieves this through a layered architecture that balances convenience with flexibility.

Three-Layer Integration Architecture

The integration consists of three distinct layers, each serving a specific purpose in connecting Refine's authentication lifecycle with better-auth's API.

Export Layer: Direct better-auth Access

The foundation of the integration begins in src/index.ts (line 7), where the package re-exports the raw betterAuth client from the better-auth library. This export ensures that developers retain full access to better-auth's server-side utilities, such as session handling and email verification, without needing to install better-auth separately.

// packages/auth/src/index.ts
export { betterAuth } from "better-auth";

By exposing the native client, the package allows advanced use cases—such as custom middleware or server-side hooks—to coexist with the higher-level Refine integration.

Client Wrapper: HTTP Endpoint Abstraction

The second layer, implemented in src/lib/auth-client.ts (lines 1-118), provides the AuthClient class—a thin wrapper around better-auth's HTTP endpoints. This class handles request building, JSON serialization, and error propagation for all authentication operations.

The AuthClient maps methods to specific better-auth endpoints:

  • Login → POST /sign-in/email
  • Register → POST /sign-up/email
  • Logout → POST /sign-out
  • Session Check → GET /session
  • Password Reset → POST endpoints for forgot and reset operations

The default base URL (/api/auth) aligns with the standard better-auth server middleware route pattern, though this is configurable during instantiation.

Refine Provider: Lifecycle Method Mapping

The top layer, located in src/providers/refine-auth-provider.tsx (lines 14-334), implements Refine's AuthProvider interface through the createRefineAuthProvider function. This factory function accepts an optional AuthClient instance (defaulting to a new client with standard configuration) and returns an object containing all required Refine authentication methods.

Each Refine lifecycle method delegates to the corresponding AuthClient method:

  • login → client.login
  • logout → client.logout
  • check → client.getSession
  • register → client.register
  • forgotPassword → client.forgotPassword

Error Handling and Response Propagation

The integration includes consistent error handling across all layers. The AuthClient catches HTTP errors and formats them into a standardized shape compatible with Refine's error handling expectations. When the provider encounters HTTP 401 or 403 responses, it optionally triggers automatic logout and redirect flows, maintaining security boundaries within the Refine application lifecycle.

The package declares better-auth as a direct dependency in packages/auth/package.json, guaranteeing compatible versions without requiring manual peer dependency management.

Implementation Examples

Basic Refine Setup

To configure Refine with the built-in provider, instantiate the auth provider and pass it to the Refine component:

import { Refine } from "@refinedev/core";
import { createRefineAuthProvider } from "@ferdiunal/refine-shadcn-auth";

const authProvider = createRefineAuthProvider(); // uses default AuthClient

function App() {
  return (
    <Refine
      authProvider={authProvider}
      /* other Refine options … */
    />
  );
}

Direct better-auth Access

For server-side operations or advanced customization, import the re-exported betterAuth client directly:

import { betterAuth } from "@ferdiunal/refine-shadcn-auth";

// Example: server-side session check in a Next.js API route
export async function GET() {
  const session = await betterAuth.getSession();
  return new Response(JSON.stringify(session), { status: 200 });
}

Custom AuthClient Configuration

When your better-auth server uses a non-standard base URL, provide a custom AuthClient:

import { createRefineAuthProvider, AuthClient } from "@ferdiunal/refine-shadcn-auth";

const customClient = new AuthClient({ baseURL: "/custom/auth" });
const authProvider = createRefineAuthProvider({ client: customClient });

Component-Level better-auth Utilities

Access better-auth methods directly within React components for features beyond the standard Refine auth flow:

import { betterAuth } from "@ferdiunal/refine-shadcn-auth";
import { useState, useEffect } from "react";

function Avatar() {
  const [url, setUrl] = useState<string>("");

  useEffect(() => {
    async function fetchAvatar() {
      const user = await betterAuth.getUser();
      setUrl(user?.avatar ?? "/default-avatar.png");
    }
    fetchAvatar();
  }, []);

  return <img src={url} alt="User avatar" />;
}

Summary

  • Three-layer architecture: The package exposes better-auth through export, wrapper, and provider layers in src/index.ts, src/lib/auth-client.ts, and src/providers/refine-auth-provider.tsx, enabling both simple integration and advanced customization.
  • Direct exports: Re-exporting betterAuth from the entry point allows server-side and advanced client use cases without separate better-auth installation.
  • HTTP abstraction: The AuthClient class standardizes communication with better-auth endpoints like /sign-in/email and /session, handling JSON bodies and credentials.
  • Refine compatibility: createRefineAuthProvider maps Refine's required methods (login, logout, check, register) to better-auth API calls, treating better-auth as a drop-in backend.
  • Configurable defaults: While defaulting to /api/auth, the AuthClient accepts custom base URLs and can be injected into the provider factory for flexible deployment scenarios.

Frequently Asked Questions

How do I configure a custom base URL for better-auth endpoints?

Pass a custom AuthClient instance to createRefineAuthProvider with your specific baseURL. By default, the client uses /api/auth, but you can override this when instantiating the client: new AuthClient({ baseURL: "/custom/auth" }). This is useful when your better-auth server middleware runs on a non-standard route.

Can I use better-auth methods not exposed by the Refine provider?

Yes. The package re-exports the raw betterAuth client from src/index.ts, allowing direct access to all better-auth utilities. Import betterAuth from @ferdiunal/refine-shadcn-auth and use it for server-side logic, custom hooks, or features outside Refine's standard auth lifecycle, such as betterAuth.getUser() or betterAuth.getSession().

What happens when the auth provider receives a 401 or 403 response?

The createRefineAuthProvider implementation catches HTTP 401 and 403 errors in its request helper and formats them into Refine's AuthError shape. Depending on your Refine configuration, these errors can trigger automatic logout and redirect flows to protect authenticated routes from unauthorized access.

Is better-auth installed as a dependency or peer dependency?

The package lists better-auth as a direct dependency in packages/auth/package.json, ensuring compatible versions are installed automatically. You do not need to add better-auth separately to your project dependencies to use the integration, though you may choose to do so for direct imports outside the package's scope.

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 →