Skip to main content
Refine provides a flexible authentication system through the Auth Provider interface. It supports any authentication method (JWT, OAuth, sessions, etc.) and integrates seamlessly with your backend.

Auth Provider Interface

The Auth Provider defines methods that Refine calls during authentication flows. From packages/core/src/contexts/auth/types.ts:66-78:
export type AuthProvider = {
  // Required methods
  login: (params: any) => Promise<AuthActionResponse>;
  logout: (params: any) => Promise<AuthActionResponse>;
  check: (params?: any) => Promise<CheckResponse>;
  onError: (error: any) => Promise<OnErrorResponse>;
  
  // Optional methods
  register?: (params: any) => Promise<AuthActionResponse>;
  forgotPassword?: (params: any) => Promise<AuthActionResponse>;
  updatePassword?: (params: any) => Promise<AuthActionResponse>;
  getPermissions?: (params?: any) => Promise<PermissionResponse>;
  getIdentity?: (params?: any) => Promise<IdentityResponse>;
};

Response Types

AuthActionResponse

Used by login, logout, register, forgotPassword, updatePassword:
type AuthActionResponse = {
  success: boolean;
  redirectTo?: string;
  error?: Error;
  successNotification?: {
    message: string;
    description?: string;
  };
  [key: string]: unknown; // Additional data
};

CheckResponse

Used by check method:
type CheckResponse = {
  authenticated: boolean;
  redirectTo?: string;
  logout?: boolean;
  error?: Error;
};

OnErrorResponse

Used by onError method:
type OnErrorResponse = {
  redirectTo?: string;
  logout?: boolean;
  error?: Error;
};

Basic Implementation

Here’s a JWT-based auth provider example: From examples/app-crm-minimal/src/providers/auth.ts:15-133:
import type { AuthProvider } from "@refinedev/core";

const API_URL = "https://api.example.com";
const TOKEN_KEY = "access_token";

export const authProvider: AuthProvider = {
  login: async ({ email, password }) => {
    try {
      const response = await fetch(`${API_URL}/auth/login`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });

      const data = await response.json();

      if (response.ok) {
        localStorage.setItem(TOKEN_KEY, data.accessToken);
        return {
          success: true,
          redirectTo: "/",
        };
      }

      return {
        success: false,
        error: {
          name: "LoginError",
          message: data.message || "Invalid credentials",
        },
      };
    } catch (error) {
      return {
        success: false,
        error: {
          name: "LoginError",
          message: "An error occurred during login",
        },
      };
    }
  },

  logout: async () => {
    localStorage.removeItem(TOKEN_KEY);
    return {
      success: true,
      redirectTo: "/login",
    };
  },

  check: async () => {
    const token = localStorage.getItem(TOKEN_KEY);

    if (!token) {
      return {
        authenticated: false,
        redirectTo: "/login",
        logout: true,
      };
    }

    try {
      // Verify token with backend
      const response = await fetch(`${API_URL}/auth/me`, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });

      if (response.ok) {
        return {
          authenticated: true,
        };
      }

      return {
        authenticated: false,
        redirectTo: "/login",
        logout: true,
      };
    } catch (error) {
      return {
        authenticated: false,
        redirectTo: "/login",
        logout: true,
      };
    }
  },

  onError: async (error) => {
    if (error.statusCode === 401 || error.statusCode === 403) {
      return {
        logout: true,
        redirectTo: "/login",
        error,
      };
    }

    return { error };
  },

  getIdentity: async () => {
    const token = localStorage.getItem(TOKEN_KEY);
    if (!token) return null;

    try {
      const response = await fetch(`${API_URL}/auth/me`, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });

      if (response.ok) {
        const user = await response.json();
        return user;
      }
    } catch (error) {
      return null;
    }

    return null;
  },

  getPermissions: async () => {
    const token = localStorage.getItem(TOKEN_KEY);
    if (!token) return null;

    try {
      const response = await fetch(`${API_URL}/auth/permissions`, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });

      if (response.ok) {
        const permissions = await response.json();
        return permissions;
      }
    } catch (error) {
      return null;
    }

    return null;
  },
};

Authentication Hooks

useLogin

Handle user login:
import { useLogin } from "@refinedev/core";

function LoginPage() {
  const { mutate: login, isLoading } = useLogin();

  const handleSubmit = (values) => {
    login(values, {
      onSuccess: (data) => {
        console.log("Login successful", data);
      },
      onError: (error) => {
        console.error("Login failed", error);
      },
    });
  };

  return (
    <Form onSubmit={handleSubmit}>
      <Input name="email" type="email" placeholder="Email" />
      <Input name="password" type="password" placeholder="Password" />
      <Button type="submit" loading={isLoading}>
        Sign In
      </Button>
    </Form>
  );
}

useLogout

Handle user logout:
import { useLogout } from "@refinedev/core";

function LogoutButton() {
  const { mutate: logout, isLoading } = useLogout();

  return (
    <button onClick={() => logout()} disabled={isLoading}>
      {isLoading ? "Logging out..." : "Logout"}
    </button>
  );
}

useIsAuthenticated

Check authentication status:
import { useIsAuthenticated } from "@refinedev/core";

function ProtectedContent() {
  const { data, isLoading } = useIsAuthenticated();

  if (isLoading) {
    return <div>Checking authentication...</div>;
  }

  if (!data?.authenticated) {
    return <div>Please login to continue</div>;
  }

  return <div>Protected content</div>;
}

useGetIdentity

Get current user information:
import { useGetIdentity } from "@refinedev/core";

function UserProfile() {
  const { data: user, isLoading } = useGetIdentity();

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{user?.email}</p>
      <img src={user?.avatar} alt={user?.name} />
    </div>
  );
}

usePermissions

Get user permissions:
import { usePermissions } from "@refinedev/core";

function AdminPanel() {
  const { data: permissions } = usePermissions();

  const isAdmin = permissions?.includes("admin");

  if (!isAdmin) {
    return <div>Access denied</div>;
  }

  return <div>Admin panel content</div>;
}

useRegister

Handle user registration:
import { useRegister } from "@refinedev/core";

function RegisterPage() {
  const { mutate: register, isLoading } = useRegister();

  const handleSubmit = (values) => {
    register(values);
  };

  return (
    <Form onSubmit={handleSubmit}>
      <Input name="email" type="email" />
      <Input name="password" type="password" />
      <Input name="confirmPassword" type="password" />
      <Button type="submit" loading={isLoading}>
        Register
      </Button>
    </Form>
  );
}

useForgotPassword / useUpdatePassword

Password reset flows:
import { useForgotPassword, useUpdatePassword } from "@refinedev/core";

function ForgotPasswordPage() {
  const { mutate: forgotPassword } = useForgotPassword();

  const handleSubmit = ({ email }) => {
    forgotPassword({ email });
  };

  return <Form onSubmit={handleSubmit}>...</Form>;
}

function ResetPasswordPage() {
  const { mutate: updatePassword } = useUpdatePassword();

  const handleSubmit = ({ password, token }) => {
    updatePassword({ password, token });
  };

  return <Form onSubmit={handleSubmit}>...</Form>;
}

Protecting Routes

Authenticated Component

Wrap routes to require authentication:
import { Authenticated } from "@refinedev/core";
import { Routes, Route, Navigate } from "react-router";

function App() {
  return (
    <Routes>
      {/* Protected routes */}
      <Route
        element={
          <Authenticated
            fallback={<Navigate to="/login" />}
            loading={<LoadingScreen />}
          >
            <Layout>
              <Outlet />
            </Layout>
          </Authenticated>
        }
      >
        <Route path="/" element={<Dashboard />} />
        <Route path="/posts" element={<PostList />} />
      </Route>

      {/* Public routes */}
      <Route path="/login" element={<LoginPage />} />
    </Routes>
  );
}
From examples/app-crm-minimal/src/App.tsx:53-62:
<Route
  element={
    <Authenticated
      key="authenticated-layout"
      fallback={<CatchAllNavigate to="/login" />}
    >
      <Layout>
        <Outlet />
      </Layout>
    </Authenticated>
  }
>
  {/* Protected routes here */}
</Route>

Redirect After Login

Redirect authenticated users away from login page:
<Route
  element={
    <Authenticated
      key="authenticated-auth"
      fallback={<Outlet />}
    >
      <NavigateToResource resource="dashboard" />
    </Authenticated>
  }
>
  <Route path="/login" element={<LoginPage />} />
</Route>
If a user visits /login while already authenticated, they’ll be redirected to the dashboard.

Authentication Flow

Error Handling

The onError method is called when API requests fail:
const authProvider: AuthProvider = {
  // ... other methods
  
  onError: async (error) => {
    // Handle 401 Unauthorized
    if (error.statusCode === 401) {
      return {
        logout: true,
        redirectTo: "/login",
        error: {
          name: "Unauthorized",
          message: "Your session has expired. Please login again.",
        },
      };
    }

    // Handle 403 Forbidden
    if (error.statusCode === 403) {
      return {
        error: {
          name: "Forbidden",
          message: "You don't have permission to perform this action.",
        },
      };
    }

    // Don't logout for other errors
    return { error };
  },
};

Common Patterns

JWT Authentication

const authProvider: AuthProvider = {
  login: async ({ email, password }) => {
    const response = await fetch(`${API_URL}/auth/login`, {
      method: "POST",
      body: JSON.stringify({ email, password }),
    });

    const { token } = await response.json();
    localStorage.setItem("token", token);

    return { success: true, redirectTo: "/" };
  },

  check: async () => {
    const token = localStorage.getItem("token");
    if (token) {
      return { authenticated: true };
    }
    return { authenticated: false, redirectTo: "/login" };
  },
};
const authProvider: AuthProvider = {
  login: async ({ email, password }) => {
    await fetch(`${API_URL}/auth/login`, {
      method: "POST",
      credentials: "include", // Include cookies
      body: JSON.stringify({ email, password }),
    });

    return { success: true, redirectTo: "/" };
  },

  check: async () => {
    const response = await fetch(`${API_URL}/auth/me`, {
      credentials: "include",
    });

    if (response.ok) {
      return { authenticated: true };
    }

    return { authenticated: false, redirectTo: "/login" };
  },

  logout: async () => {
    await fetch(`${API_URL}/auth/logout`, {
      method: "POST",
      credentials: "include",
    });

    return { success: true, redirectTo: "/login" };
  },
};

OAuth (Google, GitHub, etc.)

const authProvider: AuthProvider = {
  login: async ({ providerName }) => {
    // Redirect to OAuth provider
    window.location.href = `${API_URL}/auth/${providerName}`;
    return { success: true };
  },

  check: async () => {
    // OAuth callback will set token in URL or cookie
    const params = new URLSearchParams(window.location.search);
    const token = params.get("token");

    if (token) {
      localStorage.setItem("token", token);
      window.history.replaceState({}, document.title, "/");
      return { authenticated: true };
    }

    const storedToken = localStorage.getItem("token");
    if (storedToken) {
      return { authenticated: true };
    }

    return { authenticated: false, redirectTo: "/login" };
  },
};

Social Login UI

import { useLogin } from "@refinedev/core";

function LoginPage() {
  const { mutate: login } = useLogin();

  return (
    <div>
      <h1>Sign In</h1>
      
      <button onClick={() => login({ providerName: "google" })}>
        <GoogleIcon /> Sign in with Google
      </button>
      
      <button onClick={() => login({ providerName: "github" })}>
        <GitHubIcon /> Sign in with GitHub
      </button>
    </div>
  );
}

Advanced Patterns

Token Refresh

let refreshPromise: Promise<string> | null = null;

const refreshToken = async () => {
  if (!refreshPromise) {
    refreshPromise = fetch(`${API_URL}/auth/refresh`, {
      method: "POST",
      credentials: "include",
    })
      .then((res) => res.json())
      .then((data) => {
        localStorage.setItem("token", data.token);
        refreshPromise = null;
        return data.token;
      });
  }
  return refreshPromise;
};

const authProvider: AuthProvider = {
  check: async () => {
    const token = localStorage.getItem("token");
    if (!token) {
      return { authenticated: false, redirectTo: "/login" };
    }

    // Check if token is expired
    const payload = JSON.parse(atob(token.split(".")[1]));
    if (payload.exp * 1000 < Date.now()) {
      try {
        await refreshToken();
        return { authenticated: true };
      } catch {
        return { authenticated: false, redirectTo: "/login" };
      }
    }

    return { authenticated: true };
  },
};

Multi-Tenant Authentication

const authProvider: AuthProvider = {
  login: async ({ email, password, tenantId }) => {
    const response = await fetch(`${API_URL}/auth/login`, {
      method: "POST",
      body: JSON.stringify({ email, password, tenantId }),
    });

    const { token, tenant } = await response.json();
    localStorage.setItem("token", token);
    localStorage.setItem("tenantId", tenant.id);

    return { success: true, redirectTo: "/" };
  },

  getIdentity: async () => {
    const tenantId = localStorage.getItem("tenantId");
    const response = await fetch(`${API_URL}/auth/me`, {
      headers: {
        "X-Tenant-ID": tenantId,
      },
    });

    return response.json();
  },
};

Best Practices

1. Secure Token Storage

// Good - Use httpOnly cookies for sensitive tokens
const authProvider: AuthProvider = {
  login: async (credentials) => {
    await fetch(`${API_URL}/auth/login`, {
      method: "POST",
      credentials: "include", // Cookies are set by server
      body: JSON.stringify(credentials),
    });
  },
};

// Less secure - localStorage can be accessed by XSS
// Only use for public/non-sensitive data
localStorage.setItem("token", token);

2. Handle Token Expiration

const authProvider: AuthProvider = {
  onError: async (error) => {
    if (error.statusCode === 401) {
      // Try to refresh token
      try {
        await refreshToken();
        return { logout: false };
      } catch {
        return { logout: true, redirectTo: "/login" };
      }
    }
    return { error };
  },
};

3. Provide User Feedback

const authProvider: AuthProvider = {
  login: async (credentials) => {
    try {
      const response = await fetch(`${API_URL}/auth/login`, {
        method: "POST",
        body: JSON.stringify(credentials),
      });

      if (!response.ok) {
        const error = await response.json();
        return {
          success: false,
          error: {
            name: "LoginError",
            message: error.message || "Login failed",
          },
        };
      }

      return {
        success: true,
        redirectTo: "/",
        successNotification: {
          message: "Login successful",
          description: "Welcome back!",
        },
      };
    } catch (error) {
      return {
        success: false,
        error: {
          name: "NetworkError",
          message: "Unable to connect to server",
        },
      };
    }
  },
};

4. Type Safety

import { AuthProvider } from "@refinedev/core";

interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "user";
}

const authProvider: AuthProvider = {
  getIdentity: async (): Promise<User | null> => {
    const response = await fetch(`${API_URL}/auth/me`);
    if (response.ok) {
      return response.json();
    }
    return null;
  },
};

// Usage
function UserProfile() {
  const { data: user } = useGetIdentity<User>();
  return <div>{user?.name}</div>;
}

Next Steps

Authorization

Implement role-based access control

Auth Provider API

Complete API reference

Social Auth Tutorial

OAuth integration guide

Auth Hooks

Authentication hook reference

Build docs developers (and LLMs) love