Auth0でのロールベースアクセス制御(RBAC)

概要

やりたいこと

  • Auth0を用いたアプリケーション、正しくはAPIでユーザー毎にできることを切り分けたい。
  • ユーザーを管理者(admin)と通常メンバー(ordinary-member)に分けて管理したい。

このような管理をRBAC (Role-Based Access Control)と呼ぶらしい。

例えば、Auth0 ordinary-memberに設定された者は、メンバー情報の(一覧)取得と更新ができる。admin に設定された者 は、加えてメンバー情報の作成と削除ができるなど。

これらの設定はAuth0の管理画面からおこなうことができます。

RBACを使用できるようにする

  1. 管理画面のApplications APIs[+ Create API]で追加済の当該APISettingsを開きます。
  2. 以下の両方のトグルスイッチをONにします。
    • Enable RBAC
      • If this setting is enabled, RBAC authorization policies will be enforced for this API. Role and permission assignments will be evaluated during the login transaction.
      • この設定を有効にすると、この API に対して RBAC認可ポリシーが適用されます。ロールとパーミッションの割り当ては、ログイン処理中に評価されます。
    • Add Permissons in the Access Token
      • If this setting is enabled, the Permissions claim will be added to the access token. Only available if RBAC is enabled for this API.
      • この設定を有効にすると、アクセストークンにパーミッションクレームが追加されます。このAPIRBACが有効になっている場合のみ利用できます。

ロールを設定する

管理画面のUser ManagementRoles[+ Create Role]から次のロールを作成してみました。

NameDescription
adminなんでもできる。
ordinary-memberメンバーの一覧の取得とメンバー情報の更新ができる
※ロールなし メンバーの一覧の取得だけができる
ロールの設定

パーミッションを設定する

  1. 管理画面のApplications APIs に追加済のAPIを選択する。
  2. Permissions タブでここでは次のようなパーミッションを追加します。
Permission (Scope)Description
list:membersメンバの一覧を取得
create:membersメンバ情報を作成
update:membersメンバ情報を更新
delete:memberメンバ情報の削除
パーミッションの設定

ロールへのパーミッションの割当

  1. 管理画面のUser ManagementRoles の各ロールについて[View Detail]を選択して編集をおこなう。
  2. Permissionsタブで[Add permissions]によりAPIのパーミッションの割当をおこなう。
Add Perminssions

APIに渡されたBearerトークンの確認

APIに渡されてきたBearerトークンをjwt.ioで分解すると、次のように

画像
トークンに含まれる permissions

参考までに以下はNetlify Functionsに渡されてきたeventevent.headersを引数に取り、アクセストークン部分を切り出す関数です。

const getAccessTokenFromHeaders = (headers) => {
  const rawAuthorization = headers.authorization;
  if (!rawAuthorization) {
    return null;
  }
  const authorizationParts = rawAuthorization.split(' ');
  if (authorizationParts[0] !== 'Bearer' || authorizationParts.length !== 2) {
    return null;
  }
  return authorizationParts[1]; ← ★ここで戻す値が上図のトークン
};

アプリからロールやパーミッションを取得する

フロントエンドのコードでロールやパーミッションを取得するには、まずトークンを取得します。

:
import { useAuth0 } from '@auth0/auth0-react';

import {
  getPermissionsFromToken,
  isAdmin,
  isOrdinaryMember,
  isNoRole,
} from '../utils/permission';
:

// このあとコンポーネントのコード
  const { isAuthenticated, user, getAccessTokenSilently } = useAuth0();
  useEffect(async () => {
    if (isAuthenticated) {
      const token = await getAccessTokenSilently();
      console.table(getPermissionsFromToken(token));

   :

      if (isAdmin(token)) {
        // 管理者の場合の処理
      } else if (isOrdinaryMember(token)) {
        setRole('一般メンバ');
      } else if (isNoRole(token)) {
        // メンバ登録未完了の場合の処理
      } else {
        // 不明なロールの場合の処理
      }
    } else {
      // 認証されていない場合の処理
    }
  }, [user]);

上記の ../utils/permission の関数は次のように実装してみました。

import jwt_decode from 'jwt-decode';

export const getPermissionsFromToken = (token) => {
  const decodedToken = jwt_decode(token);
  return decodedToken.permissions;
};

export const isAdmin = (token) => {
  const permissions = getPermissionsFromToken(token);
  return permissions.includes('delete:member');
};

export const isOrdinaryMember = (token) => {
  const permissions = getPermissionsFromToken(token);
  if (permissions === null) {
    return false;
  } else if (isAdmin(token)) {
    return false;
  } else if (
    permissions.includes('list:members') &&
    permissions.includes('update:members')
  ) {
    return true;
  } else {
    return false;
  }
};

export const isNoRole = (token) => {
  const permissions = getPermissionsFromToken(token);
  return (permissions.length === 0);
}

この後の記事では、React Router v6でページを保護する方法について書いてみたいと思います。