Skip to main content
Version: 4.xx.xx

Authentication

Authentication is the process of verifying the identity of a user or client. It's a critical component of security, ensuring that only authorized users can access certain features or data within the application. Whether you are building a complex enterprise-level application or a simple CRUD interface, Refine's authentication system provides the necessary infrastructure to protect your pages and ensure that users interact with your application in a secure and controlled manner.

Refine's flexible architecture allows you to easily implement various authentication strategies:

You can implement your own authentication system or use one of the supported auth providers.

To learn more about how to create auth provider, check out the tutorial page.

Auth Provider

Refine handles authentication by Auth Provider and consumes the auth provider methods by auth hooks.

Auth provider is an object that contains methods to handles authentication in your app, designed to return promises for use with async methods. By offering a structured architecture it simplifies authentication implementation and management through your app.

To activate authentication in your app, you need to pass an authProvider to the <Refine /> as a prop. Once you provide auth provider, you can utilize our auth hooks (useLogin, useRegister, useIsAuthenticated etc.) to easily manage your authentication.

App.tsx
import { Refine, AuthProvider } from "@refinedev/core";

export const authProvider: AuthProvider = {
login: async ({ email, password }) => {
const { status } = handleLogin(email, password);

if (status === 200) {
return { success: true, redirectTo: "/dashboard" };
} else {
return {
success: false,
error: { name: "Login Error", message: "Invalid credentials" },
};
}
},
check: async (params) => ({}),
logout: async (params) => ({}),
onError: async (params) => ({}),
register: async (params) => ({}),
forgotPassword: async (params) => ({}),
updatePassword: async (params) => ({}),
getPermissions: async (params) => ({}),
getIdentity: async (params) => ({}),
};

const App = () => {
return <Refine authProvider={authProvider}>...</Refine>;
};

Handling Authentication

Refine provides a set of hooks to handle authentication. You can use these hooks to manage your authentication process. You can find the list of hooks below.

Register

Let's start with registering a new user. To register a new user, we will implement authProvider.register method. We will call this method with useRegister hook when the user submits the registration form.

import React from "react";
import { useRegister } from "@refinedev/core";

export const RegisterPage = () => {
    const { mutate: register } = useRegister();

    const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();

        // get form data
        const formData = Object.fromEntries(
            new FormData(e.currentTarget).entries(),
        );

        // call register mutation
        register(formData);

        // reset form data
        e.currentTarget.reset();
    };

    return (
        <div>
            <h1>Register</h1>
            <form onSubmit={(e) => onSubmit(e)}>
                <input
                    type="email"
                    placeholder="email"
                />
                <button type="submit">Submit</button>
            </form>
        </div>
    );
};

Login

After registering a new user, we will implement authProvider.login method to login the user. We will call this method with useLogin hook when the user submits the login form. This implementation is very similar to the registration process.

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

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

    const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();

        // get form data
        const formData = Object.fromEntries(
            new FormData(e.currentTarget).entries(),
        );

        // call login mutation
        login(formData);

        // reset form data
        e.currentTarget.reset();
    };

    return (
        <div>
            <h1>Login</h1>
            <form onSubmit={(e) => onSubmit(e)}>
                <input type="email" placeholder="email" />
                <button type="submit">Submit</button>
            </form>
        </div>
    );
};

Checking Authentication

In the previous examples, the registration and login process were set up. Next, we need to check if the user is authenticated or not. This will be done by using the authProvider.check method together with the useIsAuthenticated hook.

By using useIsAuthenticated hook, we can easily check if the user is authenticated or not. If they are, the user's profile will be shown. If not, the <Login /> component will appear.

Additionally, in this example, we will implement authProvider.logout and authProvider.getIdentity methods. We will call these methods with useLogout and useGetIdentity hooks. These hooks make it easy to log out users and get their identity information.

import React from "react";
import { useIsAuthenticated, useGetIdentity, useLogout } from "@refinedev/core";
import { LoginPage } from "./login-page.tsx";

export const HomePage = () => {
    const { data: authenticated } = useIsAuthenticated();
    const { data: identity } = useGetIdentity<{ email: string }>();
    const { mutate: logout } = useLogout();

    if (authenticated?.authenticated) {
        return (
            <div>
                <h1>Hello,  {identity?.email}</h1>
                <button onClick={() => logout()}>Logout</button>
            </div>
        );
    }

    return <LoginPage />;
};

Refine also provides <Authenticated /> component to easily handle authentication state. You can use this component to protect your routes and conditionally render your components.

To learn more about the <Authenticated /> component, check out the reference page.

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

const Page = () => {
return (
<Authenticated
loading={<div>loading...</div>}
fallback={<div>You cannot access this section</div>}
>
<h1>Welcome to your dashboard</h1>
</Authenticated>
);
};

Usage with data provider
Check the guide
Please check the guide for more information on this topic.

After implementing the authentication process, we need to inform data provider about the authentication credentials. We can do this by sending the authentication credentials with the request. For example after obtaining the authentication token we can store it in cookies and sent it with on every request.

To learn more about the how to use authentication with data provider and working example, check out data fetching guide.

Error Handling

authProvider.onError method is used to handle errors that occur during the http request.

Under the hood, Refine utilizes the useOnError hook for all data hooks. This means that when a promise is rejected from the dataProvider or when you get an error response from the API, Refine automatically calls authProvider.onError by using the useOnError hook.

Let's say wan't to get product from the API with useOne hook. If the user is not authenticated, the API will return an error response. You can handle this error by implementing authProvider.onError method and Refine will automatically call this method when the error occurs.

import React from "react";
import { useOne } from "@refinedev/core";

export const ProductPage = () => {
    const { data, refetch } = useOne<Product>({
        resource: "products",
        id: "1",
        queryOptions: {
            retry: false,
            enabled: false,
        },
    });
    const product = data?.data;

    return (
        <div>
            <h2>Product</h2>
            <p>name: {product?.name}</p>
            <button onClick={() => refetch()}>Get Product</button>
        </div>
    );
};

type Product = {
    id: string;
    name: string;
};

Once you implement authProvider.onError method, you can call this method with useOnError hook. This will help you to handle errors in single place.

UI Integrations

<AuthPage />

While Refine itself is headless, it offers <AuthPage /> Integrations for popular UI libraries for:

With <AuthPage /> component you can easily handle authentication pages (login, register, update password, forgot password) and speed up your development process.

Notification
Check the guide
Please check the guide for more information on this topic.

Refine provides a automatic notification system to notify users about the authentication errors. To use this feature, you need to pass notificationProvider to the <Refine /> component.

Once you provide notificationProvider, Refine will automatically notify users about the authentication errors on following auth provider methods:

  • register
  • login
  • logout
  • forgotPassword
  • updatePassword

For example, when you return error object from the authProvider.login method, Refine will automatically notify users about the error.

import { Refine, AuthProvider } from "@refinedev/core";
import { handleLogin } from "./utils";

export const authProvider: AuthProvider = {
login: async ({ email, password }) => {
const { status } = handleLogin(email, password);
if (status === 418) {
return {
success: false,
error: { name: "Login Error", message: "Invalid credentials" },
};
}
},
...
};

Router Integrations
Check the guide
Please check the guide for more information on this topic.

Refine provides a automatic routing system to redirect users to the desired page after the authentication process. To use this feature, you need to pass routerProvider to the <Refine /> component.

Once you provide routerProvider, Refine will automatically redirect users to the desired page on following auth provider methods:

  • register
  • login
  • logout
  • onError
  • forgotPassword
  • updatePassword

For example, when you return redirectTo object from the authProvider.register method, Refine will automatically redirect users to the desired page.

import { Refine, AuthProvider } from "@refinedev/core";
import { handleLogin } from "./utils";

export const authProvider: AuthProvider = {
register: async ({ email, password }) => {
const { status } = handleLogin(email, password);
if (status === 418) {
return {
success: false,
redirectTo: "/forgot-password",
error: { name: "Register Error", message: "User already exists" },
};
}
},
...
};

Auth hooks

HookMethodDescription
useRegisterregisterRegister a new user.
useLoginloginAuthenticate and log in a user.
useIsAuthenticatedcheckAuthCheck if the user is authenticated.
useLogoutlogoutLog out the current user.
useOnErrorhandleErrorHandle authentication errors.
useGetIdentitygetIdentityRetrieve the identity of the user.
useUpdatePasswordupdatePasswordUpdate the user's password.
useForgotPasswordforgotPasswordInitiate a password reset process.
usePermissionsgetPermissionsGet the permissions of the user.

OAuth Integrations

Flexible architecture of auth provider allows you to integrate your own or third-party authentication systems into Refine.

You can use the following oAuth provider implementations as a starting point for your own auth provider or you can use them as it is.

To learn more about the authProvider interface, check out the reference page.

Supported Auth Providers

You can use the following auth provider examples as a starting point for your own auth provider or you can use them as it is. Check the links below to see the details of each example.

  • Basic - A basic auth provider example.
  • Okta - Okta, the enterprise-grade identity management service.
  • Keycloak - An auth provider example with Keycloak.
  • Auth0 - An auth provider example with Auth0.
  • Google Auth - An auth provider example with Google Auth.
  • OTP Login - An auth provider example with OTP Login.
  • Appwrite - An auth provider example with Appwrite.
  • Supabase - An auth provider example with Supabase.
  • Strapi - An auth provider example with Strapi.
  • Basic with Nextjs - A basic auth provider example with Nextjs.
  • Basic with Remix - A basic auth provider example with Remix.
  • Kinde - An auth provider example with Kinde.

For more information on how you can create your own auth providers, refer to the Create a Auth Provider tutorial

authProvider Interface

To better understand the auth provider interface, we have created an example that demonstrates how the required methods are implemented. For more comprehensive and diverse examples, you can refer to the supported auth providers section.

To learn more about the authProvider interface, check out the reference page.

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

const authProvider: AuthProvider = {
register: async (params) => {
if (params.email === authCredentials.email && params.password) {
localStorage.setItem("email", params.email);
return {
success: true,
redirectTo: "/",
};
}
return {
success: false,
error: {
message: "Register failed",
name: "Invalid email or password",
},
};
},
login: async ({ providerName, email }) => {
if (providerName === "google") {
window.location.href = "https://accounts.google.com/o/oauth2/v2/auth";
return {
success: true,
};
}

if (providerName === "github") {
window.location.href = "https://github.com/login/oauth/authorize";
return {
success: true,
};
}

if (email === authCredentials.email) {
localStorage.setItem("email", email);
return {
success: true,
redirectTo: "/",
};
}

return {
success: false,
error: {
message: "Login failed",
name: "Invalid email or password",
},
};
},
check: async () => {
return localStorage.getItem("email")
? {
authenticated: true,
}
: {
authenticated: false,
error: {
message: "Check failed",
name: "Not authenticated",
},
logout: true,
redirectTo: "/login",
};
},
logout: async () => {
localStorage.removeItem("email");
return {
success: true,
redirectTo: "/login",
};
},
onError: async (error) => {
console.error(error);
return { error };
},
getIdentity: async () => ({
id: 1,
name: "Jane Doe",
avatar: "https://unsplash.com/photos/IWLOvomUmWU/download?force=true&w=640",
}),
updatePassword: async (params) => {
if (params.password === authCredentials.password) {
//we can update password here
return {
success: true,
};
}
return {
success: false,
error: {
message: "Update password failed",
name: "Invalid password",
},
};
},
forgotPassword: async (params) => {
if (params.email === authCredentials.email) {
//we can send email with reset password link here
return {
success: true,
};
}
return {
success: false,
error: {
message: "Forgot password failed",
name: "Invalid email",
},
};
},
getPermissions: async (params) => {
if (params) {
// do some logic like for example you can get roles for specific tenant
return ["admin"];
}

return ["admin"];
},
};