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:
- Amazon Cognito
- Okta (Included in Refine's Enterprise Edition)
- Auth0
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.
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.
Code Example
Dependencies: @refinedev/core@latest,axios@^1.6.2
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> ); }
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> ); };
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"); }, };
Content: import React from "react"; import { DataProvider } from "@refinedev/core"; export const dataProvider = (url: string): DataProvider => ({ getList: async () => { throw new Error("Not implemented"); }, getOne: async () => { throw new Error("Not implemented"); }, create: async () => { throw new Error("Not implemented"); }, update: async () => { throw new Error("Not implemented"); }, deleteOne: async () => { throw new Error("Not implemented"); }, getApiUrl: () => url, });
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.
Code Example
Dependencies: @refinedev/core@latest,axios@^1.6.2
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> ); }
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> ); };
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"); }, };
Content: import React from "react"; import { DataProvider } from "@refinedev/core"; export const dataProvider = (url: string): DataProvider => ({ getList: async () => { throw new Error("Not implemented"); }, getOne: async () => { throw new Error("Not implemented"); }, create: async () => { throw new Error("Not implemented"); }, update: async () => { throw new Error("Not implemented"); }, deleteOne: async () => { throw new Error("Not implemented"); }, getApiUrl: () => url, });
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.
Code Example
Dependencies: @refinedev/core@latest,axios@^1.6.2
Content: import React from "react"; import { Refine } from "@refinedev/core"; import { HomePage } from "./home-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} > <HomePage /> </Refine> ); }
Content: 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 />; };
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> ); };
Content: import React from "react"; import { AuthProvider } from "@refinedev/core"; // 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. export const authProvider: AuthProvider = { login: async ({ email }) => { localStorage.setItem("email", email); return { success: true, }; }, check: async () => { const email = localStorage.getItem("email"); if (!email) { return { authenticated: false, }; } return { authenticated: true, }; }, logout: async () => { localStorage.removeItem("email"); return { success: true, }; }, getIdentity: async () => { const email = localStorage.getItem("email"); return { email, }; }, register: async () => { throw new Error("Not implemented"); }, onError: async () => { throw new Error("Not implemented"); }, };
Content: import React from "react"; import { DataProvider } from "@refinedev/core"; export const dataProvider = (url: string): DataProvider => ({ getList: async () => { throw new Error("Not implemented"); }, getOne: async () => { throw new Error("Not implemented"); }, create: async () => { throw new Error("Not implemented"); }, update: async () => { throw new Error("Not implemented"); }, deleteOne: async () => { throw new Error("Not implemented"); }, getApiUrl: () => url, });
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
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.
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.
Code Example
Dependencies: @refinedev/core@latest,axios@^1.6.2
Content: import React from "react"; import { Refine } from "@refinedev/core"; import { ProductPage } from "./product-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} > <ProductPage /> </Refine> ); }
Content: 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; };
Content: import React from "react"; import { AuthProvider } from "@refinedev/core"; export const authProvider: AuthProvider = { onError: async (error: HttpError) => { // simulating a HTTP error if (error.statusCode === 401) { alert(error.message); } return {}; }, login: async () => { throw new Error("Not implemented"); }, logout: async () => { throw new Error("Not implemented"); }, check: async () => { throw new Error("Not implemented"); }, };
Content: import React from "react"; import { DataProvider } from "@refinedev/core"; export const dataProvider = (url: string): DataProvider => ({ getOne: async () => { // simulating a HTTP error const error: HttpError = { message: "User is not authenticated", statusCode: 401, }; return Promise.reject(error); }, getList: async () => { throw new Error("Not implemented"); }, create: async () => { throw new Error("Not implemented"); }, update: async () => { throw new Error("Not implemented"); }, deleteOne: async () => { throw new Error("Not implemented"); }, getApiUrl: () => url, });
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.
- Headless
- Ant Design
- Material UI
- Chakra UI
- Mantine
Headless
Code Example
Dependencies: @refinedev/core@latest,@refinedev/simple-rest@latest,@refinedev/react-router@latest,react-router@^7.0.2
Content: import React from "react"; import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { Refine, AuthPage, Authenticated, ErrorComponent } from "@refinedev/core"; import routerProvider, { CatchAllNavigate, NavigateToResource, } from "@refinedev/react-router"; import dataProvider from "@refinedev/simple-rest"; export default function App() { return ( <BrowserRouter> <Refine routerProvider={routerProvider} dataProvider={dataProvider("https://api.fake-rest.refine.dev")} authProvider={{ check: async () => ({ authenticated: false, redirectTo: "/login", }), login: async () => { return { success: false, }; }, logout: async () => { return { success: false, }; }, onError: async () => ({}), getIdentity: async () => ({ id: 1, name: "John Doe", avatar: "https://i.pravatar.cc/300", }), }} resources={[ { name: "dashboard", list: "/", }, ]} > <Routes> <Route element={ <Authenticated fallback={<CatchAllNavigate to="/login" />} > <Outlet /> </Authenticated> } > <Route index element={<div>Welcome!</div>} /> </Route> <Route element={ <Authenticated fallback={ <div style={{ margin: "24px auto", maxWidth: "400px", }} > <Outlet /> </div> } > <NavigateToResource resource="dashboard" /> </Authenticated> } > <Route path="/login" element={<AuthPage type="login" />} /> <Route path="/register" element={<AuthPage type="register" />} /> <Route path="/forgot-password" element={<AuthPage type="forgotPassword" />} /> <Route path="/update-password" element={<AuthPage type="updatePassword" />} /> </Route> <Route element={ <Authenticated> <Outlet /> </Authenticated> } > <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> </Refine> </BrowserRouter> ); }
Ant Design
Code Example
Dependencies: @refinedev/antd@latest,@refinedev/core@latest,@refinedev/simple-rest@latest,@refinedev/react-router@latest,react-router@^7.0.2,antd@^5.0.5
Content: import React from "react"; import "@refinedev/antd/dist/reset.css"; import { App as AntdApp, ConfigProvider } from "antd"; import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { AuthPage, ErrorComponent, RefineThemes, ThemedLayoutV2, } from "@refinedev/antd"; import { Authenticated, Refine } from "@refinedev/core"; import routerProvider, { CatchAllNavigate, NavigateToResource, } from "@refinedev/react-router"; import dataProvider from "@refinedev/simple-rest"; export default function App() { return ( <BrowserRouter> <ConfigProvider theme={RefineThemes.Blue}> <AntdApp> <Refine routerProvider={routerProvider} dataProvider={dataProvider( "https://api.fake-rest.refine.dev", )} authProvider={{ check: async () => ({ authenticated: false, redirectTo: "/login", }), login: async () => { return { success: false, }; }, logout: async () => { return { success: false, }; }, onError: async () => ({}), getIdentity: async () => ({ id: 1, name: "John Doe", avatar: "https://i.pravatar.cc/300", }), }} resources={[ { name: "dashboard", list: "/", }, ]} options={{ syncWithLocation: true }} > <Routes> <Route element={ <Authenticated fallback={ <CatchAllNavigate to="/login" /> } > <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route index element={<div>Welcome!</div>} /> </Route> <Route element={ <Authenticated fallback={<Outlet />}> <NavigateToResource resource="dashboard" /> </Authenticated> } > <Route path="/login" element={ <AuthPage type="login" wrapperProps={{ style: { paddingTop: 24, }, }} /> } /> <Route path="/register" element={ <AuthPage type="register" wrapperProps={{ style: { paddingTop: 24, }, }} /> } /> <Route path="/forgot-password" element={ <AuthPage type="forgotPassword" wrapperProps={{ style: { paddingTop: 24, }, }} /> } /> <Route path="/update-password" element={ <AuthPage type="updatePassword" wrapperProps={{ style: { paddingTop: 24, }, }} /> } /> </Route> <Route element={ <Authenticated> <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> </Refine> </AntdApp> </ConfigProvider> </BrowserRouter> ); }
Material UI
Code Example
Dependencies: @refinedev/antd@latest,@refinedev/core@latest,@refinedev/simple-rest@latest,@refinedev/react-router@latest,@refinedev/mui@5.0.0,react-router@^7.0.2,@emotion/react@^11.8.2,@emotion/styled@^11.8.1,@mui/lab@^6.0.0-beta.14,@mui/material@^6.1.7,@mui/system@latest,@mui/x-data-grid@^7.22.2
Content: import React from "react"; import CssBaseline from "@mui/material/CssBaseline"; import GlobalStyles from "@mui/material/GlobalStyles"; import { ThemeProvider } from "@mui/material/styles"; import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { Authenticated, Refine } from "@refinedev/core"; import { AuthPage, ErrorComponent, RefineThemes, ThemedLayoutV2, } from "@refinedev/mui"; import routerProvider, { CatchAllNavigate, NavigateToResource, } from "@refinedev/react-router"; import dataProvider from "@refinedev/simple-rest"; export default function App() { return ( <BrowserRouter> <ThemeProvider theme={RefineThemes.Blue}> <CssBaseline /> <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} /> <Refine routerProvider={routerProvider} dataProvider={dataProvider( "https://api.fake-rest.refine.dev", )} authProvider={{ check: async () => ({ authenticated: false, redirectTo: "/login", }), login: async () => { return { success: false, }; }, logout: async () => { return { success: false, }; }, onError: async () => ({}), getIdentity: async () => ({ id: 1, name: "John Doe", avatar: "https://i.pravatar.cc/300", }), }} resources={[ { name: "dashboard", list: "/", }, ]} options={{ syncWithLocation: true }} > <Routes> <Route element={ <Authenticated fallback={<CatchAllNavigate to="/login" />} > <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route path="/dashboard" element={<div>Welcome</div>} /> </Route> <Route element={ <Authenticated fallback={<Outlet />}> <NavigateToResource resource="dashboard" /> </Authenticated> } > <Route path="/login" element={ <AuthPage type="login" wrapperProps={{ style: { margin: "72px 0px", height: "max-content", }, }} /> } /> <Route path="/register" element={ <AuthPage type="register" wrapperProps={{ style: { margin: "72px 0px", height: "max-content", }, }} /> } /> <Route path="/forgot-password" element={ <AuthPage type="forgotPassword" wrapperProps={{ style: { margin: "72px 0px", height: "max-content", }, }} /> } /> <Route path="/update-password" element={ <AuthPage type="updatePassword" wrapperProps={{ style: { margin: "72px 0px", height: "max-content", }, }} /> } /> </Route> <Route element={ <Authenticated> <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> </Refine> </ThemeProvider> </BrowserRouter> ); }
Chakra UI
Code Example
Dependencies: @refinedev/chakra-ui@latest,@refinedev/core@latest,@refinedev/simple-rest@latest,@refinedev/react-router@latest,@refinedev/react-table@latest,react-router@^7.0.2,@tabler/icons-react@^3.1.0,@chakra-ui/react@^2.5.1
Content: import React from "react"; import { ChakraProvider } from "@chakra-ui/react"; import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { AuthPage, ErrorComponent, RefineThemes, ThemedLayoutV2, } from "@refinedev/chakra-ui"; import { Authenticated, Refine } from "@refinedev/core"; import routerProvider, { CatchAllNavigate, NavigateToResource, } from "@refinedev/react-router"; import dataProvider from "@refinedev/simple-rest"; export default function App() { return ( <BrowserRouter> <ChakraProvider theme={RefineThemes.Blue}> <Refine routerProvider={routerProvider} dataProvider={dataProvider( "https://api.fake-rest.refine.dev", )} authProvider={{ check: async () => ({ authenticated: false, redirectTo: "/login", }), login: async () => { return { success: false, }; }, logout: async () => { return { success: false, }; }, onError: async () => ({}), getIdentity: async () => ({ id: 1, name: "John Doe", avatar: "https://i.pravatar.cc/300", }), }} resources={[ { name: "dashboard", list: "/", }, ]} options={{ syncWithLocation: true }} > <Routes> <Route element={ <Authenticated fallback={<CatchAllNavigate to="/login" />} > <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route index element={<div>Welcome!</div>} /> </Route> <Route element={ <Authenticated fallback={<Outlet />}> <NavigateToResource resource="dashboard" /> </Authenticated> } > <Route path="/login" element={ <AuthPage type="login" wrapperProps={{ style: { margin: "24px 0px", height: "max-content", }, }} /> } /> <Route path="/register" element={ <AuthPage type="register" wrapperProps={{ style: { margin: "24px 0px", height: "max-content", }, }} /> } /> <Route path="/forgot-password" element={ <AuthPage type="forgotPassword" wrapperProps={{ style: { margin: "24px 0px", height: "max-content", }, }} /> } /> <Route path="/update-password" element={ <AuthPage type="updatePassword" wrapperProps={{ style: { margin: "24px 0px", height: "max-content", }, }} /> } /> </Route> <Route element={ <Authenticated> <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> </Refine> </ChakraProvider> </BrowserRouter> ); }
Mantine
Code Example
Dependencies: @refinedev/mantine@latest,@refinedev/core@latest,@refinedev/simple-rest@latest,@refinedev/react-router@latest,@refinedev/react-table@latest,react-router@^7.0.2,@tabler/icons-react@^3.1.0,@emotion/react@^11.8.2,@mantine/core@^5.10.4,@mantine/hooks@^5.10.4,@mantine/notifications@^5.10.4
Content: import React from "react"; import { Global, MantineProvider } from "@mantine/core"; import { NotificationsProvider } from "@mantine/notifications"; import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { Authenticated, Refine } from "@refinedev/core"; import { AuthPage, ErrorComponent, RefineThemes, ThemedLayoutV2, } from "@refinedev/mantine"; import routerProvider, { CatchAllNavigate, NavigateToResource, } from "@refinedev/react-router"; import dataProvider from "@refinedev/simple-rest"; export default function App() { return ( <BrowserRouter> <MantineProvider theme={RefineThemes.Blue} withNormalizeCSS withGlobalStyles > <Global styles={{ body: { WebkitFontSmoothing: "auto" } }} /> <NotificationsProvider position="top-right"> <Refine routerProvider={routerProvider} dataProvider={dataProvider( "https://api.fake-rest.refine.dev", )} authProvider={{ check: async () => ({ authenticated: false, redirectTo: "/login", }), login: async () => { return { success: false, }; }, logout: async () => { return { success: false, }; }, onError: async () => ({}), getIdentity: async () => ({ id: 1, name: "John Doe", avatar: "https://i.pravatar.cc/300", }), }} resources={[ { name: "dashboard", list: "/", }, ]} options={{ syncWithLocation: true }} > <Routes> <Route element={ <Authenticated fallback={ <CatchAllNavigate to="/login" /> } > <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route index element={<div>Welcome</div>} /> </Route> <Route element={ <Authenticated fallback={<Outlet />}> <NavigateToResource resource="dashboard" /> </Authenticated> } > <Route path="/login" element={ <AuthPage type="login" wrapperProps={{ style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", margin: "24px auto", }, }} /> } /> <Route path="/register" element={ <AuthPage type="register" wrapperProps={{ style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", margin: "24px auto", }, }} /> } /> <Route path="/forgot-password" element={ <AuthPage type="forgotPassword" wrapperProps={{ style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", margin: "24px auto", }, }} /> } /> <Route path="/update-password" element={ <AuthPage type="updatePassword" wrapperProps={{ style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", margin: "24px auto", }, }} /> } /> </Route> <Route element={ <Authenticated> <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> </Refine> </NotificationsProvider> </MantineProvider> </BrowserRouter> ); }
Notification
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
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
Hook | Method | Description |
---|---|---|
useRegister | register | Register a new user. |
useLogin | login | Authenticate and log in a user. |
useIsAuthenticated | checkAuth | Check if the user is authenticated. |
useLogout | logout | Log out the current user. |
useOnError | handleError | Handle authentication errors. |
useGetIdentity | getIdentity | Retrieve the identity of the user. |
useUpdatePassword | updatePassword | Update the user's password. |
useForgotPassword | forgotPassword | Initiate a password reset process. |
usePermissions | getPermissions | Get 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"];
},
};