Auth Provider Interface
The Auth Provider defines methods that Refine calls during authentication flows. Frompackages/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 bylogin, logout, register, forgotPassword, updatePassword:
type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
successNotification?: {
message: string;
description?: string;
};
[key: string]: unknown; // Additional data
};
CheckResponse
Used bycheck method:
type CheckResponse = {
authenticated: boolean;
redirectTo?: string;
logout?: boolean;
error?: Error;
};
OnErrorResponse
Used byonError method:
type OnErrorResponse = {
redirectTo?: string;
logout?: boolean;
error?: Error;
};
Basic Implementation
Here’s a JWT-based auth provider example: Fromexamples/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>
);
}
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
TheonError 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" };
},
};
Cookie-Based Sessions
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