Blog

How to build a user management dashboard with WorkOS and Node

Step-by-step tutorial on how to add basic user management functionality to your app using Node.js and WorkOS.


Once you have users in your app, you must start managing them. You need a dashboard screen where you can list your users, change their permissions, revoke their access, or invite new ones.

In this tutorial, we will see how you can do all of the above with WorkOS and Node.js.

If you haven’t implemented user authentication yet, then do that first. You can use one of these guides to get started:

Prerequisites

To follow this tutorial, you will need the following:

Step 1: Install the SDK

Install the WorkOS Node SDK to your app.

Via npm:

	
npm install @workos-inc/node
	

Via yarn:

	
yarn add @workos-inc/node
	

Via pnpm:

	
pnpm add @workos-inc/node
	

Step 2: Set secrets

To make calls to WorkOS, you must authenticate using the WorkOS API key and client ID. Copy these values from the WorkOS dashboard.

Store the values as managed secrets and pass them to the SDK either as environment variables or directly in your app’s configuration.

Environment variables example:

	
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
	

For more info on how to handle secrets safely see Best practices for secrets management.

Step 3: Set up the backend

We are ready to start adding code. We will implement the following functionality:

  • List users.
  • Remove users that should no longer have access.
  • Change what role a user has.
  • Invite new users (with the ability to re-send and revoke invitations).

We will present two different implementations.

The first option uses WorkOS Widgets: React components that provide complete functionality for common enterprise app workflows. The Users Management Widget provides a UI for inviting, removing and editing users and is available for free to all AuthKit customers.

Here is a quick demo on how Widgets work:

If you prefer to build and manage your own authentication UI, or you don’t want to use Widgets, follow the second option. It will show you how to implement this functionality via the User Management API.

Option 1: Use Widgets

1. Install the Widgets package

First, install the @workos-inc/widgets package from the npm registry, along with its peer dependencies:

	
npm install @workos-inc/widgets @radix-ui/themes @tanstack/react-query
	

2. Configure CORS

Because WorkOS widgets issue client-side requests to WorkOS, it is necessary to configure your site as an allowed web origin. Go to WorkOS dashboard> Authentication > Cross-Origin Resource Sharing (CORS) > Configure CORS and add your app’s URL.

3. Add the widget

Widgets need to authenticate with the WorkOS API and for that they need a token. How you will get one depends on whether you use AuthKit or not.

If you are using the authkit-js or authkit-react libraries, you can use the provided access token, returned by getAccessToken. Use this code sample to get a token and add the widget to your app:

	
import { useAuth } from '@workos-inc/authkit-react';
import { UsersManagement, WorkOsWidgets } from '@workos-inc/widgets';

export function UserTable() {
  // get a token
  const { isLoading, user, getAccessToken } = useAuth();
  if (isLoading) {
    return '...';
  }
  if (!user) {
    return 'Logged in user is required';
  }

  // add the widget
  return (
    <WorkOsWidgets>
      <UsersManagement authToken={getAccessToken} />
    </WorkOsWidgets>
  );
}
	

If you use one of our backend SDKs, use the get token method in the SDK to request a token. You need to specify which widget you want the token for (using scopes) and who is the user using the widget (using userId and organizationId).

Use this code sample to get a token and add the User Management widget to your app:

	
import { UsersManagement, WorkOsWidgets } from '@workos-inc/widgets';

// get a token
const authToken = await workos.widgets.getToken({
  userId: user.id,
  organizationId,
  scopes: ['widgets:users-table:manage'],
});

//add the widget
export function UserTable({ token }) {
  return (
    <WorkOsWidgets>
      <UsersManagement authToken={token} />
    </WorkOsWidgets>
  );
}
	

Note the following to avoid errors:

  • Widget tokens expire after one hour.
  • To successfully generate a token, the user must have the correct permissions. New WorkOS accounts are created with an “Admin” role that has all Widget permissions assigned, but existing accounts will need to be updated. In order to use the User Management widget, a user must have a role that has the widgets:users-table:manage permission. This can be done on the “Roles” page of the WorkOS dashboard. For more info see the Roles and Permissions docs.

That’s it, your user management dashboard is ready to go!

4. Customize the UI (optional)

WorkOS Widgets are powered by Radix Themes, which are styled out-of-the box when you import its CSS in your app.

	
import '@radix-ui/themes/styles.css';
	

You can customize Widgets by passing a theme prop to WorkOSWidgets. This prop accepts an object with the same options as Radix themes.

	
function UsersTable({ authToken }) {
  return (
    <WorkOsWidgets
      theme={{
        appearance: 'dark',
        accentColor: 'green',
        radius: 'medium',
        fontFamily: 'Inter',
      }}
    >
      <UsersManagement authToken={authToken} />
    </WorkOsWidgets>
  );
}
	

You can also style Widgets using CSS. Start with the layout.css stylesheet from Radix Themes, as well as the base.css stylesheet from WorkOS Widgets, to get a base level of functional styling without opinionated design choices.

	
import '@radix-ui/themes/layout.css'; 
import '@workos-inc/widgets/base.css';
	

Individual elements in Radix themes are accessible via CSS class selectors prefixed with woswidgets-. For example, you can add your own button styles by selecting the woswidgets-button class.

	
.woswidgets-button {
  border-radius: 4px;
  color: hsl(0 0% 100%);
  background-color: hsl(272deg 81% 56%);
  background-image: linear-gradient(
    to top right,
    hsl(272deg 81% 56%),
    hsl(271deg 91% 65%)
  );
  /* ... */
}
	

Option 2: Manual implementation

Follow this section if you prefer to build and manage your own authentication UI, or you don’t want to use Widgets. We will not go into the frontend details but will see how to implement this functionality in the backend via the User Management Authentication API.

We will build the following functionality:

  • List users.
  • Remove a user.
  • Update a user’s role.
  • Invite a new user.

List users

We will start with listing all the users that belong to the same organization as the logged in user.

An organization is a collection of users that also acts as a container for enterprise features (like SSO). By enabling a feature for a specific organization, you enable it for all users who are members of this organization. This way, you can enable features like forcing all users that use a specific email domain to use a specific SSO connection. For more information on organizations and how to use them, see Model your B2B SaaS with organizations.

You can list users using the listUsers() SDK method.

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const users = await workos.userManagement.listUsers();
	

You can also set as input parameters the organizationId (if you want to list users of another organization) or the email (if you want filter users by their email).

This is what the result looks like for the example we just saw:

	
{
  "data": [
    {
      "object": "user",
      "id": "user_01E4ZCR3C56J083X43JQXF3JK5",
      "email": "marcelina.davis@example.com",
      "first_name": "Marcelina",
      "last_name": "Davis",
      "email_verified": true,
      "profile_picture_url": "https://workoscdn.com/images/v1/123abc",
      "created_at": "2021-06-25T19:07:33.155Z",
      "updated_at": "2021-06-25T19:07:33.155Z"
    }
  ],
  "list_metadata": {
    "before": "user_01E4ZCR3C56J083X43JQXF3JK5",
    "after": "user_01EJBGJT2PC6638TN5Y380M40Z"
  }
}
	

Note that since an org can have many users, this endpoint is paginated. WorkOS pagination uses 4 parameters:

  • limit: How many objects to return (1-100). The default value is 10.
  • order: Order the results by the creation time. Supported values are "asc" and "desc" for showing older and newer records first respectively. Default order is descending.
  • before and after:Object IDs that define your place in the list. When the IDs are not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with "obj_123", your subsequent call can include after="obj_123" to fetch a new batch of objects before or after "obj_123".

Remove user

We will continue with adding an endpoint that removes a user from the org. For that we will use the deleteOrganizationMembership() SDK method.

When a user is added to the org an org membership is created. Each membership has a unique ID and you need to know it in order to remove the user from that org:

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

await workos.userManagement.deleteOrganizationMembership(
  'ORGANIZATION_MEMBERSHIP_ID',
);
	

To get the organization membership ID, you can list the orgs a user belongs to using listOrganizationMemberships():

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const organizationMemberships =
  await workos.userManagement.listOrganizationMemberships({
    userId: 'USER_ID',
  });
	

Assuming you know the organization ID, you can parse the results, read the membership ID, and use it to remove the user from the org:

	
{
  "data": [
    {
      "object": "organization_membership",
      "id": "om_01E4ZCR3C56J083X43JQXF3JK5", // organization membership ID
      "user_id": "user_01E4ZCR3C5A4QZ2Z2JQXGKZJ9E",
      "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5",
      "role": {
        "slug": "member"
      },
      "status": "active",
      "created_at": "2021-06-25T19:07:33.155Z",
      "updated_at": "2021-06-25T19:07:33.155Z"
    }
  ],
  "list_metadata": {
    "before": "om_01E4ZCR3C56J083X43JQXF3JK5",
    "after": "om_01EJBGJT2PC6638TN5Y380M40Z"
  }
}
	

With this flow the user will remain in your system but will not have access to that organization. If this is not the behavior you want, you also have this options:

List a user’s roles

To retrieve and display a user’s role, use the listOrganizationMemberships() SDK method:

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const organizationMemberships =
  await workos.userManagement.listOrganizationMemberships({
    userId: 'USER_ID',
    // You can also filter further using the org ID
    // organizationId: 'ORG_ID',
  });
	

Update a user’s roles

To change a user’s roles for a specific org, you must know the organization membership ID. Use the updateOrganizationMembership() SDK method:

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const organizationMembership =
  await workos.userManagement.updateOrganizationMembership(
    'ORGANIZATION_MEMBERSHIP_ID',
    {
      roleSlug: 'admin',
    },
  );
	

To get the organization membership ID, list the orgs the user belongs to.

Invite new users

You can invite users to sign up for your app and join an org using the sendInvitation() SDK method:

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const invitation = await workos.userManagement.sendInvitation({
  email: 'marcelina@example.com',
});
	

Other input parameters you can use are:

  • organizationId: The ID of the organization that the recipient will join.
  • expiresInDays: How many days the invitations will be valid for (range=1-30, default=7).
  • inviterUserId: The ID of the user who invites the recipient. The invitation email will mention the name of this user.
  • roleSlug: The role that the recipient will receive when they join the organization in the invitation.

If you don’t set an organizationId, the user will be able to join the application but won’t be added to any org.

If you want to re-send an invitation you have to first revoke it, and then send a new one.

Revoke an invitation

To revoke an invitation you have sent out, use the revokeInvitation() SDK method:

	
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('WORKOS_API_KEY');

const invitation = await workos.userManagement.revokeInvitation(
  'INVITATION_ID',
);
	

You can list an organization’s invitations to find the ID of the one you are looking for.

Next steps

You have now built basic user management functionality in your app. This might not be exciting but it’s basic functionality that every app needs.

Assuming that this was maybe your second step, with the first being authentication, there are still many more in your journey into the authentication and authorization world. The next steps can be things like handling the user’s session, implementing access control, provisioning users automatically, handling failed authentication events, and more. Stay tuned for follow up tutorials on all of these areas.

Here are some resources you might find useful:

In this article

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.