Mastering RBAC in Logto: A Comprehensive Real-World Example

This article offers a comprehensive guide on mastering Role-Based Access Control (RBAC) in Logto, using a real-world example of an online bookstore to explore key user roles, scopes, and integrating Logto's RBAC features in frontend and backend applications for enhanced security and access control.
Sijie
SijieDeveloper
April 28, 202311 min read
Mastering RBAC in Logto: A Comprehensive Real-World Example

Introduction

Access control and security are essential aspects of modern applications, ensuring that users have appropriate access to resources. Logto's Role-Based Access Control (RBAC) offers developers an efficient way to manage access control and security in their applications. In this article, we'll explore the powerful features of Logto's RBAC implementation using a real-world example, helping you understand and apply these concepts to your projects.

By examining both frontend and backend code snippets, you'll gain a comprehensive perspective on integrating Logto's RBAC into your application stack. By the end of this article, you'll be well-equipped to harness Logto's RBAC features to enhance your project's security and access control.

Introducing BookHarber: An online bookstore use case

To effectively demonstrate Logto's RBAC features, we'll use a real-world example: BookHarber, an online bookstore. BookHarber offers a wide range of features for customers and staff, ensuring a seamless and secure shopping experience.

Key features of BookHarber include:

  1. Browsing and Purchasing Books: Users can easily search for and purchase books from a diverse collection, spanning various genres and authors.
  2. Order Management and Logistics Tracking: Registered customers can manage their orders, track shipping, and receive updates on their purchases.
  3. Special Offers and Holiday Activities: BookHarber provides exclusive discounts and promotions during special events and holidays to engage and reward its customer base.
  4. Customer Support: Customers can open support tickets to address any concerns or issues they may encounter, receiving prompt assistance from BookHarber staff.
  5. Customer Management: Staff members with different roles have the ability to manage various aspects of the platform, such as customer accounts, order processing, and issue resolution.

Roles

In the BookHarber ecosystem, we can identify several key user roles, such as:

  1. Guest: Unregistered users who can browse the website, search for books, and view special offers.
  2. Customer: Registered users who can purchase books, manage orders, track logistics, and open support tickets.
  3. Store Admin: Staff members responsible for overseeing the overall management and operations of the platform. With full access.
  4. Books Manager: Staff members in charge of book and category management.
  5. Customer Service Agent: Staff members tasked with responding to support tickets.
  6. Third-Party Logistics Provider: External partners responsible for managing and tracking the shipping and delivery of orders.
  7. Marketing Staff: Staff members responsible for promoting BookHarber, responsible for managing special offers and events.

Designing scopes for BookHarber REST APIs

To effectively implement Logto's RBAC system for BookHarber, we need to design scopes that correspond to the various REST APIs. Scopes are permissions that define the level of access a specific role has for each API endpoint. By assigning the appropriate scopes to each user role, we can ensure that users only have access to the actions and resources relevant to their role.

Let's design scopes for the following REST APIs:

  1. Categories API:
    • create:categories: POST /categories
    • write:categories: PUT /categories/:id
    • delete:categories: DELETE /categories/:id
    • list:categories: GET /categories
  2. Books API:
    • create:books: POST /books
    • write:books: PUT /books/:id
    • delete:books: DELETE /books/:id
    • list:books: GET /books
    • read:books: GET /books/:id
  3. Customers API:
    • list:customers: GET /customers
    • write:customers: PUT /customers/:id
    • delete:customers: DELETE /customers/:id
    • read:customers: GET /customers/:id
  4. Orders API:
    • create:orders: POST /orders
    • list:orders: GET /orders
    • read:orders: GET /orders/:id
    • write:orders: PUT /orders/:id
  5. Events API:
    • create:events: POST /events
    • write:events: PUT /events/:id
    • list:events: GET /events
    • delete:events: DELETE /events/:id
  6. Order Tracks API:
    • read:orderTracks: GET /orders/:id/tracks
    • create:orderTracks: POST /orders/:id/tracks
    • write:orderTracks: PUT /orders/:id/tracks/:trackId
  7. Tickets API:
    • create:tickets: POST /tickets
    • list:tickets: GET /tickets
    • read:tickets: GET /tickets/:id
    • write:tickets: PUT /tickets/:id

Assigning scopes to Roles

Now that we have defined the appropriate scopes for each REST API, we can assign these scopes to the respective user roles in the BookHarber ecosystem:

ScopesGuestCustomerStore AdminBooks ManagerCustomer Service AgentThird-Party Logistics ProviderMarketing Staff
create:categories
write:categories
delete:categories
list:categories
create:books
write:books
delete:books
list:books
read:books
list:customers
write:customers
delete:customers
read:customers
create:orders
list:orders
read:orders
write:orders
create:events
write:events
list:events
delete:events
read:orderTracks
create:orderTracks
write:orderTracks
create:tickets
list:tickets
read:tickets
write:tickets

Understanding the differences between "list" and "read" scopes

To further illustrate the differences between "list" and "read" scopes in the context of REST API design and RBAC, let's consider a real-world example involving an online bookstore, BookHarber.

Suppose BookHarber has two types of users: customers and customer service agents. Customers can create orders, while customer service agents are responsible for helping customers with their orders. Let's take a look at how "list" and "read" scopes apply to the orders API resource in this scenario.

  1. List Scopes: A "list" scope allows the user to access a collection of entities in the system. For example, the list:orders scope permits a user to retrieve a list of all available orders. In the context of BookHarber, this scope could be useful for store administrators or other staff members who need to have an overview of all orders in the system. However, customer service agents should not be able to access the entire list of orders, as their role is to assist individual customers with their specific orders.
  2. Read Scopes: A "read" scope grants the user permission to access a single entity with a given ID. For instance, the read:orders scope allows the user to view detailed information about a specific order by its ID. In BookHarber's case, this scope is ideal for customer service agents who need to access information about a particular customer's order. When a customer opens a support ticket, the customer service agent can use the order ID provided in the ticket to access and view the details of that specific order.

Understanding ownership: Why customers don't need "read" or "list" scopes for their own orders

In many applications, it is common for users to have access to their own resources without explicitly granting them the corresponding "read" or "list" scopes. This is because users are considered the owners of these resources and should naturally have access to them. In the case of our BookHarber example, customers can create orders but do not possess the "read:orders" or "list:orders" scopes.

The concept of ownership plays a crucial role in defining access control for specific resources in a REST API. By acknowledging that users can always access their own resources, we can implement more efficient and secure access control without granting unnecessary permissions. In BookHarber's case, this means that customers can still view and manage their orders without needing any additional scopes.

To demonstrate how this works, let's consider the GET /orders endpoint:

  1. If a user has the list:orders scope (e.g., store administrators or staff members), they will be able to view all orders in the system. This provides them with a comprehensive view of the order data necessary for their role.
  2. If a user does not have the list:orders scope (e.g., regular customers), the system will only return the orders that belong to the user. This ensures that customers can still access their order information without being granted unnecessary permissions.

By implementing this ownership-based access control, the API can provide the appropriate level of access to different user roles while maintaining security and a tailored user experience. In BookHarber's scenario, the ownership model allows customers to access their order information without the need for "read:orders" or "list:orders" scopes, simplifying the access control design and enhancing the overall user experience.

Configuring settings in Logto Console

To complete the configuration in Logto's Console, follow these steps:

  1. Create a Single Page Application (SPA) for React: Set up an SPA in the Logto Console for your React application.
  2. Create an API Resource: Add a new API Resource with the identifier https://api.bookharber.com.
  3. Define Scopes for the Resource: Create the necessary scopes under the newly created API Resource.
  4. Create Roles and Assign Scopes: Define the user roles for your application, and assign the appropriate scopes to each role.
  5. Assign Roles to Users: Assign the relevant roles to the users in your application, ensuring that each user (Especially staff member) has the correct permissions based on their role.

Protect API using scopes

In our example project, BookHarber, we use Express for the backend service and React for the frontend web page. This section will provide a brief overview of how we can integrate Logto's RBAC features into these popular technologies to secure our application.

The full doc: https://docs.logto.io/docs/recipes/rbac/protect-resource

Frontend

To initialize Logto in your React application, follow the documentation provided here:: https://docs.logto.io/docs/recipes/integrate-logto/react/

In addition to the basic setup, you will need to specify the "resource" and "scopes" in the configuration:

const config: LogtoConfig = {
  endpoint: '<your-logto-endpoint>', // E.g. http://localhost:3001
  appId: '<your-application-id>',
  resources: ['https://api.bookharber.com/'],
  // For "customer" user
  scopes: [
    'list:categories',
    'list:books',
    'read:books',
    'create:orders',
    'create:tickets',
    'list:events',
  ],
};

Here's an example of how to make an API request using Logto:

const { getAccessToken } = useLogto();

const getCategories = async () => {
  const accessToken = await getAccessToken();
  const response = await fetch('https://api.bookharber.com/categories', {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  });
  const categories = await response.json();
  return categories;
};

Backend

To protect API, follow: https://docs.logto.io/docs/recipes/protect-your-api/

In addition to the example code (https://docs.logto.io/docs/recipes/protect-your-api/node), we’ll need to add the scope validation:

async function parseUserScope(req, res, next) {
  const { payload } = await jwtVerify(/*...*/);
	req.scopes = payload.scope?.split(' ') || [];
  next();
}

function verifyScope(scope) {
  return (req, res, next) => {
    if (req.scopes.includes(scope)) {
      next();
    } else {
      return res
        .status(403)
        .json({ message: "Forbidden: Insufficient scope." });
    }
  };
}

app.get('/tickets', parseUserScope, verifyScope('list:tickets'), (req, res) => {
  // Fetch all tickets
  // ...
  res.status(200).json({ tickets: /*...*/ });
});

app.get('/orders', parseUserScope, (req, res) => {
  if (req.scopes.includes('list:orders')) {
		const orders = await getAllOrders();
    res.status(200).json({ orders });
  } else {
    const orders = await getUserOrders(req.user.id);
    res.status(200).json({ orders });
  }
});

Conclusion

Logto's RBAC system is a powerful tool for managing access control and security in modern applications. By leveraging Logto's RBAC features, you can ensure that users have appropriate access to resources based on their roles, protecting sensitive data and functionality from unauthorized access.

In this article, we explored a real-world example of an online bookstore, BookHarber, and demonstrated how to design scopes, assign them to user roles, and implement Logto's RBAC features in both the frontend and backend of the application.

By applying these concepts and techniques to your projects, you can enhance the security and access control of your applications, providing a seamless and secure user experience. Whether you're working on an e-commerce platform, a content management system, or any other project requiring role-based access control, Logto's RBAC system offers a flexible and efficient solution to meet your access control needs.