Getting started with RBAC

Before You Start

In order to complete this guide, you'll need the following:

  • A Stytch B2B project. If you don't have one already, in the Dashboard, click on your existing project name in the top left corner of the Dashboard, click Create a new project, and then select B2B Authentication.
  • The project Test environment's project_id and secret from the API keys section. You'll need to pass these values into the Authorization request header for every Stytch API call.
  • An Organization, Members, and an authentication flow of your choosing setup. Guides for Email Magic Link, OAuth, SSO and Password login can be found in the navigation bar.

Step 1: Configure Roles, Resources and Actions in the Stytch Dashboard

Log in to the Stytch dashboard and navigate to the Roles & Permissions page. The switcher under Test Environment allows you to switch between the Roles and Resources tabs.

Roles and Resources Switcher

Under Roles you will see the two default Stytch Roles that are pre-configured with a set of permissions related to Stytch actions:

  • stytch_member – this Role is assigned to all Members automatically
  • stytch_admin – this Role is automatically assigned to Members who create a new Organization via Discovery

Under Resources you will see the 4 default Stytch Resources:

  • stytch.organization – contains all permissions relating to acting upon the Organization entity
  • stytch.member – contains all permissions related to acting upon other Members within the Organization, including creating, updating, and deleting Members within the Organization
  • stytch.sso – contains all permissions related to configuring SSO Connections for the Organization
  • stytch.self – contains all permissions related to updating the logged-in Member's entity

See the Stytch Resources & Roles guide for more information about the default Resources and Roles.

In order to test both Stytch authorization permissions as well as your own custom authorization permissions, we will create a new Resource and associated actions.

Click "Create Resource" and create a Resource with Resource ID set to document, and with the following actions: create, update, delete, and download.

Add Actions

Your new custom Resource should look like this:

Save Resource

Navigate back to the Roles tab and create a new Role with the Role ID set to limited_admin. Click the "Add new" button under "Permissions". Assign the limited_admin Role the create and download permissions for your new custom document Resource.

Assign Permissions

Click "Add another Resource", select the stytch.organization Resource, and give this Role permission for the update.info.name action - which will allow Members with this Role to update their Organization's name.

Assign Multiple Permissions

Your new custom Role should look like this:

Save Role

You can view the RBAC policy for your Project at any time by accessing the Get RBAC Policy endpoint. The policy contains a complete list of Resources and Roles. Please see the endpoint documentation for more information. The SDKs will automatically load your project's RBAC policy and refresh it periodically in order to ensure up-to-date authorization enforcement.

Step 2: Assign Role to Member

You can assign Roles to Members through the dashboard or through the UpdateMember endpoint. Below is an example of explicitly assigning the limited_admin Role to a specific Member via the API.

curl --request PUT \
	--url https://test.stytch.com/v1/b2b/organizations/organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931/members/member-test-32fc5024-9c09-4da3-bd2e-c9ce4da9375f \
	-u 'PROJECT_ID:SECRET' \
	-H 'Content-Type: application/json' \
	-d '{
		"roles": ["limited_admin"]
	}'

In the dashboard you can do this by navigating to a specific Member’s page and clicking “Edit Settings”. Under the "Explicit Roles" section, select “Assign Role” and select the limited_admin Role.

Assigning Role

Your Member should look like this after adding the Role:

Member Object

Roles can also be assigned implicitly via an attribute of the Member. You can implicitly assign Roles to Members based on their SSO Connection, their SSO Connection Group, or their Email Domain (this covers Members logging in through any email-based method, including Magic Links, OAuth, and Passwords). Clicking on individual Organizations in the Organizations tab of our dashboard will allow you to set implicit role assignments based off email domains, SSO Connections, and SSO Connection Groups.

You will need to create an SSO Connection before creating role assignments based off of them. Once the implicit role assignments are created, any Member who is created or updated to have these attributes will automatically be granted the corresponding role assignment.

See the role assignment guide for more information about role assignment.

Step 3: Log in as Member and get a Member Session

Login as the Member you added Roles to above, using any of the available methods. You will receive back a Member Session from the successful authentication response.

The response will include the roles array, which shows all Roles that the Member possesses through this Member Session, either through explicit or implicit assignment:

{
  "member_session": {
    "member_session_id": "session-test-fe6c042b-6286-479f-8a4f-b046a6c46509",
    "expires_at": "2023-08-10T07:41:52Z",
    "started_at": "2023-08-09T07:41:52Z",
    "last_accessed_at": "2023-08-09T07:41:52Z",
    "expires_at": "2023-08-10T07:41:52Z",
    "member_id": "member-test-32fc5024-9c09-4da3-bd2e-c9ce4da9375f",
    "organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
    "roles": ["stytch_member", "limited_admin"],
    "authentication_factors": [
      {
        "delivery_method": "email",
        "email_factor": {
          "email_address": "claude.shannon@stytch.com"
        }
      }
    ]
  }
}

The Roles on the Member Session may differ from the Roles you see on the Member object - Roles that are implicitly assigned by SSO connection or SSO group will only be valid for a Member Session if there is at least one authentication factor on the Member Session from the specified SSO connection.

Step 4: Authorization Enforcement on Stytch Resources

Stytch's RBAC implementation offers out-of-the-box handling of authorization checks for certain endpoints that involve actions on Members, Organizations, and SSO connections. For these endpoints, if you pass in a header containing a session_token or a session_jwt for an unexpired Member Session, we will check that the Member Session has the necessary permissions to call the endpoint. If the Member Session does not contain a Role that satisfies the requested permissions, or if the Member's Organization does not match the organization_id passed in the request, a 403 error will be thrown. Otherwise, the request will proceed as normal.

These methods are also available in our Backend SDKs. We’ve updated our Node, Go, Python, and Ruby SDKs with new options to send session information in headers. Our typed libraries have new {Method}RequestOptions that can be included on the relevant methods to enforce authorization.

To test the case where the Member requests an action they are authorized to take, we will test updating the name of the Member's Organization - a permission that the Member has because we assigned the stytch.organization:update.info.name permission to thelimited_admin Role earlier. To test this, call the UpdateOrganization API endpoint and include the Member's Session in the Headers. You can include either a session token under the X-Stytch-Member-Session header or a session JWT under the X-Stytch-Session-JWT header.

curl --request PUT \
  --url https://test.stytch.com/v1/b2b/organizations/organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931 \
  -u 'PROJECT_ID:SECRET' \
  -H 'Content-Type: application/json' \
  -H 'X-Stytch-Member-Session: "mZAYn5aLEqKUlZ_Ad9U_fWr38GaAQ1oFAhT8ds245v7Q"' \
  -d '{
    "name": "Hungarian Mathematicians"
  }'

This call will succeed, and you will receive the updated Organization object back.

To test the case where the Member requests an action they are not authorized to take, we will call SendInviteEmail to invite a new Member to the Organization. This endpoint requires the Member Session to have permission to perform the create action on the stytch.member Resource.

curl --request POST \
  --url https://test.stytch.com/v1/b2b/magic_links/email/invite \
  -u 'PROJECT_ID:SECRET' \
  -H 'Content-Type: application/json' \
  -H 'X-Stytch-Session-JWT: "eyJ..."'
  -d '{
    "organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
    "email_address": "paul.erdos@stytch.com"
  }'

This call will return a 403 error, and the request will not be processed.

For a full list of Stytch endpoints that can enforce RBAC, see the Authorization checks guide.

Step 5: Authorization Enforcement of Custom Permissions

API

To check if a Member has permission to perform an action on one of your custom Resources, you can call the SessionsAuthenticate endpoint and include an authorization_check parameter.

To test the case where the Member is authorized, pass in the Member's organization_id, the document resource ID, and the download action:

curl --request POST \
  --url https://test.stytch.com/v1/b2b/sessions/authenticate \
  -u 'PROJECT_ID:SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "session_jwt": "eyJ...",
    "authorization_check": {
        "organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
        "resource_id": "document",
        "action": "download",
    },
  }'

Since we gave the limited_admin Role permission to perform the download action on the document Resource, this call should succeed.

To test the case where the Member is not authorized, pass in the Member's organization_id, the document resource ID, and the delete action:

curl --request POST \
  --url https://test.stytch.com/v1/b2b/sessions/authenticate \
  -u 'PROJECT_ID:SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "session_jwt": "eyJ...",
    "authorization_check": {
        "organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
        "resource_id": "document",
        "action": "delete",
    },
  }'

Since we did not give the limited_admin Role permission to perform the delete action on the document Resource, this call should return a 403 error.

Backend SDKs

If you use session JWTs, our backend SDKs enable you to make low-latency localized authorization decisions.

In general, the authenticate method always makes a network request to the SessionsAuthenticate API endpoint. The authenticateJWT method will try to authenticate the session JWT locally and will fall back to a network request if the session JWT is expired. The authenticateJWTLocal method will always perform local authentication and will throw an error if the session JWT is expired.

With the introduction of RBAC, the authenticate, authenticateJWT and authenticateJWTLocal methods can all perform both authentication and authorization. In order to perform an authorization check along with the authentication check, simply pass in the authorization_check parameter (format varies depending on language) to any of the three methods.

Local authorization uses a locally cached copy of the project's RBAC policy. The policy will be refreshed via a network request if an authorization check is performed and the locally cached policy was last refreshed more than 5 minutes ago. This means that if you are not using RBAC, the policy will never be refreshed and you will not incur any additional latency from network calls.

Let's consider an example with the Node backend SDK (on version 10.0.0 or higher). To test the case where the Member is authorized, pass in the Member's organization_id, the document resource ID, and the download action:

const stytch = require('stytch');

const client = new stytch.B2BClient({
  project_id: 'PROJECT_ID',
  secret: 'SECRET',
});

const authz_check = {
	organization_id: 'organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931',
	resource_id: 'document',
	action: 'download',
}

const params = {
  	session_jwt: 'eyJ...',
	authorization_check: authz_check
};

client.sessions.authenticateJwt(params)
  .then(resp => { console.log(resp) })
  .catch(err => { console.log(err) });

Since we gave the limited_admin Role permission to perform the download action on the document Resource, this call should succeed.

To test the case where the Member is authorized, pass in the Member's organization_id, the document resource ID, and the delete action. This should throw a 403 error.

Step 6: Authorization Enforcement in the frontend SDKs

Authorization Enforcement on Stytch Resources

As mentioned in Step 4, Stytch's RBAC implementation offers out-of-the-box handling of authorization checks for certain endpoints that involve actions on Members, Organizations, and SSO connections. If you call these endpoints through the API or through the backend SDKs, you must pass in a session token or session JWT as a header (for the API) or as an options parameter (for the backend SDKs).

In the frontend SDKs, these methods can only be called by authenticated end users. The frontend SDKs will automatically enforce the authorization checks, ensuring that the logged-in Member has the necessary permissions to perform the requested action. You do not need to pass in any additional authorization check parameters. To use these methods, Member actions & permissions must be enabled in the SDK configuration page of the Stytch dashboard.

If you do not plan on using RBAC, methods that act upon the logged-in Member object - Update Self, Delete Self password, and Delete Self MFA phone number - will still work. This is because all Members are assigned the stytch_member role, and by default, the stytch_member role has all permissions on the stytch.self object. However, if you'd like to, for example, call UpdateMember on a different Member, the logged-in Member must have appropriate permissions to do so.

Helper Methods for Custom Resources

While we strongly recommend always performing a backend authorization evaluation, we also offer an option to perform client side authorization checks in order to reduce network calls and properly render the UI based on the Member’s permissions.

We offer two methods for determining Authorization status – isAuthorizedSync, which uses a locally cached version of the RBAC policy, and isAuthorized, which calls the API to refresh the RBAC policy.

See the Javascript SDK Docs and React Native SDK docs for more specific details about these functions. The following is an example of using the React useStytchIsAuthorized hook to perform a permissions check:

import { useStytchIsAuthorized } from '@stytch/react/b2b';

export const EditDocuments = () => {
  const { isAuthorized } = useStytchIsAuthorized('documents', 'edit');

  const editDocument = () => {
    //...
  };

  return <button disabled={!isAuthorized} onClick={editDocument}>Edit</button>;
};

The frontend SDKs also offer an allPermissions method, which returns the complete list of permissions currently granted to the logged-in Member.

See the Javascript SDK Docs and React Native SDK docs for more specific details about this function. The following is an example of using the React withStytchPermissions HOC to load all permissions:

import { withStytchPermissions } from '@stytch/react/b2b';

const MyComponent = (props) => {
  const canEditDocuments = props.stytchPermissions.document.edit;
  const canReadImages = props.stytchPermissions.image.read;

  return (
    <>
      <button disabled={!canEditDocuments} onClick={editDocument}>Edit</button>;
      <button disabled={!canReadImages} onClick={viewImages}>View Images</button>;
    </>
  )
}

return withStytchPermissions<Permissions>(MyComponent);