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:
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.
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.
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.
importReactfrom"react";import{useRegister}from"@refinedev/core";exportconstRegisterPage = ()=>{const{mutate:register} = useRegister();constonSubmit = (e: React.FormEvent<HTMLFormElement>)=>{e.preventDefault();// get form dataconstformData = Object.fromEntries(newFormData(e.currentTarget).entries(),);// call register mutationregister(formData);// reset form datae.currentTarget.reset();};return(<div><h1>Register</h1><formonSubmit={(e)=>onSubmit(e)}><inputtype="email"placeholder="email"/><buttontype="submit">Submit</button></form></div>);};
Content: import React from "react";
import { Refine } from "@refinedev/core";
import { RegisterPage } from "./register-page.tsx";
import { dataProvider } from "./data-provider.ts";
import { authProvider } from "./auth-provider.ts";
const API_URL = "https://api.fake-rest.refine.dev";
export default function App() {
return (
<Refine
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
authProvider={authProvider}
>
<RegisterPage />
</Refine>
);
}
File: /register-page.tsx
Content: 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>
);
};
File: /auth-provider.ts
Content: import React from "react";
import { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = {
register: async ({ email }) => {
// to keep the example short and simple, we didn't send a request, and we save the token in localStorage.
// in real world, you should send a request and token should be saved in more secure place.
localStorage.setItem("token", email);
alert("You have successfully registered!");
return {
success: true,
};
},
login: async () => {
throw new Error("Not implemented");
},
logout: async () => {
throw new Error("Not implemented");
},
check: async () => {
throw new Error("Not implemented");
},
onError: async () => {
throw new Error("Not implemented");
},
};
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.
importReactfrom"react";import{useLogin}from"@refinedev/core";exportconstLoginPage = ()=>{const{mutate:login} = useLogin();constonSubmit = (e: React.FormEvent<HTMLFormElement>)=>{e.preventDefault();// get form dataconstformData = Object.fromEntries(newFormData(e.currentTarget).entries(),);// call login mutationlogin(formData);// reset form datae.currentTarget.reset();};return(<div><h1>Login</h1><formonSubmit={(e)=>onSubmit(e)}><inputtype="email"placeholder="email"/><buttontype="submit">Submit</button></form></div>);};
Content: import React from "react";
import { Refine } from "@refinedev/core";
import { LoginPage } from "./login-page.tsx";
import { dataProvider } from "./data-provider.ts";
import { authProvider } from "./auth-provider.ts";
const API_URL = "https://api.fake-rest.refine.dev";
export default function App() {
return (
<Refine
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
authProvider={authProvider}
>
<LoginPage />
</Refine>
);
}
File: /login-page.tsx
Content: 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>
);
};
File: /auth-provider.ts
Content: import React from "react";
import { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = {
login: async ({ email }) => {
// to keep the example short and simple, we didn't send a request, and we save the token in localStorage.
// in real world, you should send a request and token should be saved in more secure place.
localStorage.setItem("token", email);
alert("You are logged in!");
return {
success: true,
};
throw new Error("Not implemented");
},
register: async () => {
throw new Error("Not implemented");
},
logout: async () => {
throw new Error("Not implemented");
},
check: async () => {
throw new Error("Not implemented");
},
onError: async () => {
throw new Error("Not implemented");
},
};
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.
Refine also provides <Authenticated /> component to easily handle authentication state. You can use this component to protect your routes and conditionally render your components.
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.
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.
With <AuthPage /> component you can easily handle authentication pages (login, register, update password, forgot password) and speed up your development process.
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.
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.
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.
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.