Skip to main content
Version: 4.xx.xx

Authorization

Authorization is a key aspect of security and user experience in web applications. Whether you are building a complex enterprise-level application or a simple CRUD interface, Refine's authorization system provides the necessary infrastructure to protect your resources and ensure that users interact with your application in a secure and controlled manner.

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

  • Role-Based Access Control (RBAC)
  • Attribute-Based Access Control (ABAC)
  • Access Control List (ACL)

With any authorization solution. (i.e. Okta, Casbin, Cerbos, or more)

Refine offers several features to help you implement authorization in your application:

  • <CanAccess /> component: Conditionally renders child components based on the user's access to a resource.
  • useCan hook: Returns a value indicating whether the user has access to a resource based on the given parameters.
  • UI Integrations: Conditionally renders UI elements such as buttons, menu items, etc. based on the user's access to a resource.

In order to enable these features, Refine uses the Access Control Provider as an interface to connect your application with your authorization solution and provides necessary parameters to make access control decisions.

Access Control Provider

The Access Control Provider is an object that contains a can method. This method is called by Refine to understand if the user can see a certain resource or perform an action.

A basic Access Control Provider looks like this:

access-control-provider.ts
import { AccessControlProvider } from "@refinedev/core";

export const accessControlProvider: AccessControlProvider = {
can: async ({ resource, action, params }) => {
console.log(resource); // products, orders, etc.
console.log(action); // list, edit, delete, etc.
console.log(params); // { id: 1 }, { id: 2 }, etc.

if (meetSomeCondition) {
return { can: true };
}

return {
can: false,
reason: "Unauthorized",
};
},
};

And can be passed to <Refine /> component's accessControlProvider prop:

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

import { accessControlProvider } from "./access-control-provider";

export const App = () => {
return (
<Refine
accessControlProvider={accessControlProvider}
>
{/* ... */}
</Refine>
);
};

To learn more about the Access Control Provider, check out the reference page.

CanAccess Component

The CanAccess component can be used to wrap your pages or components to hide them from unauthorized users.

It calls Access Control Provider's can method and conditionally renders its children based on the result.

Here's a basic example of how to use the CanAccess component:

list-page.tsx
import { CanAccess } from "@refinedev/core";

export const ListPage = () => {
return (
<CanAccess
resource="products"
action="list"
fallback={<h1>You are not authorized to see this page.</h1>}
>
<>
<h1>Products</h1>
<CanAccess resource="products" action="show" params={{ id: 1 }}>
<Button>See Details</Button>
</CanAccess>
</>
</CanAccess>
);
};

To learn more about the CanAccess component, check out the reference page.

Router Integrations

Refine's router integrations can infer resource, action, and params.id props from the current route and pass them to the <CanAccess /> component.

This means you can wrap all of your routes with a single <CanAccess /> component, instead of wrapping each page individually.

See React Router, Next.js, Remix integration pages for more information.

useCan Hook

The useCan hook can be used to check if the user has access to a resource or action.

It calls Access Control Provider's can method and returns a value indicating whether the user has access to the resource or action.

Here's a basic example of how to use the useCan hook:

list-page.tsx
import { useCan } from "@refinedev/core";

export const ListPage = () => {
const { data } = useCan({
resource: "products",
action: "show",
params: { id: 1 },
});

return (
<>
<h1>Products</h1>
{data?.can && <Button>See Details</Button>}
</>
);
};

To learn more about the useCan hook, check out the reference page.

Handling Authorization

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

const role = "editor";
// Uncomment this line and refresh to see difference.
// const role = "admin";

export const accessControlProvider: AccessControlProvider = {
  can: async ({ action, resource, params }) => {
    console.log(action, resource, params);

    if (
      role === "admin" &&
      ["field", "refund", "approve"].includes(action)
    ) {
      return {
        can: true,
      };
    }

    return {
      can: false,
      reason: "Unauthorized",
    };
  },
};

UI Integrations

When Access Control Provider is provided, Refine's UI Integrations automatically manages the visibility of their components like buttons and menu items, simplifying the management of UI.

These UI Integrations uses the Access Control Provider to check if a user has the necessary permissions. This check is performed without requiring manual implementation for each component, streamlining the development process.

Sider

Sider component's menu items will automatically hidden if user don't have access.

Let's assume we have products resource.

Menu item of this resource will call can method with following parameters:

{ resource: "products", action: "list" }

And if user isn't allowed to list products, menu item will be hidden.

Buttons

If you are using one of our buttons from our UI Integrations in your application, you don't need to wrap it with <CanAccess /> or use useCan every time. These buttons will automatically be shown or hidden.

button-example.ts
// Following buttons call `can` method with commented parameters.
import {
CreateButton, // { resource: "products", action: "create", params: { resource }}
ListButton, // { resource: "products", action: "list" , params: { resource }}
EditButton, // { resource: "products", action: "edit", params: { id: 1, resource } }
ShowButton, // { resource: "products", action: "show", params: { id: 1, resource } }
DeleteButton, // { resource: "products", action: "delete", params: { id: 1, resource } }
CloneButton, // { resource: "products", action: "clone", params: { id: 1, resource } }
} from "@refinedev/antd"; // or @refinedev/chakra-ui, @refinedev/mui, @refinedev/mantine