developers

Blazor B2B SaaSアプリケーションにロールベースのアクセス制御を実装する方法

Auth0 Organizationsを活用するB2B SaaSアプリケーションに、ロールベースのアクセス制御(RBAC)を追加する方法を解説します。

B2B SaaSでは、個別の顧客組織に対して分離されカスタマイズされた体験を提供する機能が最重要です。Auth0 Organizationsは、専用のユーザーベース、カスタムブランディング、組織固有の設定などの機能を提供し、マルチテナント環境を管理するための堅牢なフレームワークです。この基盤の上にAuth0 Organizations内でロールベースのアクセス制御(RBAC)を実装すると、ユーザー権限の管理を実現できます。

本記事では、一連の記事で構築しているSaaS Blazorアプリケーションを使い、Auth0 Organizationsのフレームワークにロールを適合させ、プログラムで管理する方法を探ります。

Auth0 Organizationsにおけるロール管理の理解

Auth0では、ロールと権限を定義して、ロールベースのアクセス制御認可モデルを適用できます。通常、ユーザーに割り当てられたロールはAuth0テナント全体に適用されます。例えば、editor ロールが割り当てられたユーザーは、Auth0 Actions経由で別の認可ポリシーを適用しない限り、Auth0テナントに登録されている全てのアプリケーションでこのロールを保持します。

Auth0 Organizationsでは、顧客のユーザーをAuth0テナント内で個別のエンティティとして表現できます。この分離は、組織ごとにユーザー、接続、ポリシーを管理するために重要です。Auth0 Organizationのコンテキストでは、ロールは特定のOrganization で定義され、スコープが設定されます。この重要な違いにより、メンバーが指定されたOrganization で何ができるかをきめ細かく制御できます。

SaaSアプリケーションを利用する2つの顧客、Acme Inc.とFoo Corp.がいるシナリオを考えます。Auth0 Organizationsを使い、Acme Inc.のユーザーをFoo Corp.のユーザーから分離しました。しかし、アプリケーション用に設定したログインフローと適用している接続モデルによっては、特定の状況が発生します。

例えば、ユーザーが個人としても組織のユーザーとしてもログインできるように設定しているとします。その場合、どちらのロールを考慮すべきかという問題が生じます。実際、ユーザーは個人としてログインする際はアプリケーションの管理者であっても、Acme Inc.のユーザーとしてログインする際は単なるメンバーになる可能性があります。

同様に、ユーザーがAcme Inc.とFoo Corp.の両方のユーザーであるように組織間で共有される場合、ログインするOrganizationに基づいてユーザーが異なるロールを持つ可能性があります。例えば、ユーザーはAcme Inc.にログインする際は管理者で、Foo Corp.にログインする際はただのメンバーというシナリオです。

そのため、Organizationに紐づいたロールを持つ必要があります。

本記事では、従来のロールベースのアクセス制御について議論しますが、より複雑なB2B SaaSのシナリオでは、認可チェックのためによりきめ細やかな粒度が必要になる場合があります。その場合は、Fine-Grained Authorizationを活用できます。

Auth0 Organizationのロールを作成する

Auth0 Organizationでユーザーに割り当てるロールの作成は簡単です。Auth0ダッシュボードまたはAuth0 Management APIを使用できます。どちらの場合も、基本的なステップは2つです。

  1. テナントでアプリケーションが必要とするロールを作成します
  2. 1つ以上のロールをOrganizationのメンバーに割り当てます

本記事では、Auth0 Management APIを使用してロールを作成し、Organizationのメンバーに割り当てます。

サンプルアプリケーション

使用するシナリオは、以前の記事でBlazorを使用して構築したB2B SaaSアプリケーションです。以下の記事で、アプリケーションの構築方法をまとめています。

以下のコマンドを実行して、開始用のBlazorプロジェクトをダウンロードできます。

git clone --branch email-verification --single-branch https://github.com/andychiare/MyBlazorSaaS

Auth0でアプリケーションを登録・設定するには、添付のREADMEファイルの指示に従ってください。Auth0アカウントをお持ちでない場合は、無料でサインアップできます

Blazorアプリケーションでは、新規顧客がサブスクライブして新しいOrganizationを作成できますが、現在ユーザー間の区別はありません。アプリケーションに2つのロールを導入します。

  • admin ロールは、アプリケーションをサブスクライブしたユーザーに自動的に割り当てられ、管理ダッシュボードへのアクセスを許可します。
  • member ロールは、管理ダッシュボードへアクセスできないユーザーに割り当てられます。

この例では、通常ロールに割り当てられる権限は使用しません。シンプルにするため、アクセス制御はロール名に焦点を当てています。

A new Auth0-powered .NET app in less than a minute? Try out our templates

Install the Nugget packageAuth0 Templates for .NET

Auth0 Organizationのロールを作成する

アプリケーションコードを修正する前に、ロール管理に必要なアプリケーションの権限を更新してください。READMEファイルに従ってアプリケーションを設定したと仮定し、Auth0ダッシュボードに移動して、Applications > ApplicationsセクションでManagementClient アプリケーションを見つけます。

アプリケーション設定ページのAPIsタブを選択し、Auth0 Management APIアイテムの下のエリアを展開します。次に、以下の権限を選択して既存のものに追加します。

read:rolescreate:rolesupdate:organizationscreate:organization_member_rolesread:organization_member_roles

最後に、Updateボタンをクリックして変更を適用します。

これで、adminmemberロールがまだ作成されていない場合に作成できるようにアプリケーションを変更できます。adminロールは、新しいOrganization内で新しく作成されたユーザーに割り当てられます。

このため、MyBlazorSaaS/Components/Pages/Onboardingフォルダ内のCreateOrganization.razorファイルを開き、以下で強調表示されている変更を適用します。

//...existing code..
private async Task StartCreateOrganization()
{
    if (!String.IsNullOrEmpty(OrganizationName))
    {
        Organization organization;

        try {
            //...existing code...

            // 👇 new code
            //Create admin role if it does not exist
            Role adminRole;
            var adminRoleList = await managementClient.Roles.GetAllAsync(
                new GetRolesRequest {NameFilter = "admin"}
            );

            if (adminRoleList.Count == 0)
            {
                adminRole = await managementClient.Roles.CreateAsync(
                    new RoleCreateRequest
                    {
                        Name = "admin",
                        Description = "Manage the organization's configuration."
                    }
                );
            } else {
                adminRole = adminRoleList[0];
            }

            //Create member role if it does not exist
            Role memberRole;
            var memberRoleList = await managementClient.Roles.GetAllAsync(
                new GetRolesRequest {NameFilter = "member"}
            );

            if (memberRoleList.Count == 0)
            {
                memberRole = await managementClient.Roles.CreateAsync(
                    new RoleCreateRequest
                    {
                        Name = "member",
                        Description = "Member of an organization."
                    }
                );
            } else {
                memberRole = memberRoleList[0];
            }
            // 👆 new code

            //Add member
            await managementClient.Organizations.AddMembersAsync(
                organization.Id,
                new OrganizationAddMembersRequest() {
                    Members = [UserId]
                }
            );

            // 👇 new code
            //Assign admin role to the new member
            await managementClient.Organizations.AddMemberRolesAsync(
                organization.Id,
                UserId,
                new OrganizationAddMemberRolesRequest {
                    Roles = [adminRole.Id]
                }
            );
            // 👆 new code

            //...existing code...
        } else {
            Message = "Organization name is required.";
        }
    }

//...existing code...

最初のコードブロックは2つのロールがまだ存在しない場合に作成します。2番目のコードブロックは、現在のOrganizationのコンテキストでユーザーにadminロールを割り当てます。コードを詳しく見てみましょう。

//Create admin role if it does not exist
Role adminRole;
var adminRoleList = await managementClient.Roles.GetAllAsync(
    new GetRolesRequest {NameFilter = "admin"}
);

if (adminRoleList.Count == 0)
{
    adminRole = await managementClient.Roles.CreateAsync(
        new RoleCreateRequest
        {
            Name = "admin",
            Description = "Manage the organization's configuration."
        }
    );
} else {
    adminRole = adminRoleList[0];
}

この部分はadminロールを作成します。adminRole変数を定義し、RolesオブジェクトのGetAllAsync()メソッドを使用してAuth0テナントからadminロールを取得します。adminという名前のロールのみを取得するためにパラメータとして渡されるフィルタに注意してください。

ロール管理の詳細については、Management APIリファレンスドキュメントを確認してください。

Auth0テナントにadminロールがまだ存在しない場合、RolesオブジェクトのCreateAsync()メソッドを使用して作成します。この場合、ロールの名前と説明を含むRoleCreateRequestインスタンスを渡します。

Auth0テナントにadminロールがすでに存在する場合は、単にadminRole変数に割り当てるだけです。

同様に、残りのコードでmemberロールを作成または取得します。

Auth0ダッシュボードを使用してSaaSアプリケーションのロールを作成することもできます。その場合、上記で示したコードは必要ありません。adminロールを取得し、作成しようとしているユーザーに割り当てるだけで済みます。

新しいOrganizationのユーザーを作成した後、次のコードでadminロールを割り当てます。

// 👇 new code
//Assign admin role to the new member
await managementClient.Organizations.AddMemberRolesAsync(
    organization.Id,
    UserId,
    new OrganizationAddMemberRolesRequest {
        Roles = [adminRole.Id]
    }
);
// 👆 new code

OrganizationsオブジェクトのAddMemberRolesAsync()メソッドを使用し、Organization ID、ユーザーID、およびOrganizationAddMemberRolesRequestインスタンスに埋め込まれたロールIDを渡しました。メソッドのパラメータの詳細については、ドキュメントリファレンスをお読みください。

B2B SaaSアプリケーションに、Organizationのユーザーとロールができました。

ロールベースのアクセス制御を適用する

Organizationのコンテキストでロールのサポートができたら、それらを活用してアプリケーションへのアクセスを制御します。

ダッシュボードの構築と保護

前述のように、管理者ユーザーにはダッシュボードへのアクセスを許可し、Organizationメンバーには許可しません。

MyBlazorSaaS/Components/PagesフォルダにDashboardフォルダを作成し、次の内容でHome.razorファイルを追加します。

@page "/dashboard"
@attribute [Authorize(Roles = "admin")]

@inject IAuth0Management auth0Management

<PageTitle>Dashboard</PageTitle>

<h1>Welcome to your dashboard!</h1>

<div>
    <div class="form-group row my-2">
        <label for="organizationName" class="col-sm-4 col-form-label">Organization</label>
        <div class="col-sm-6">
            <InputText
                type="text"
                @bind-Value="OrganizationName"
                id="organizationName"
                class="form-control"
                disabled/>
        </div>
    </div>
</div>

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? authenticationState { get; set; }
    private string OrganizationName = "";

    protected override async Task OnInitializedAsync()
    {
        if (authenticationState is not null)
        {
            var state = await authenticationState;

            if (state.User?.Identity?.IsAuthenticated??false)
            {
                var organizationId = state.User?.FindFirst(c => c.Type == "org_id")?.Value??String.Empty;
                if (!string.IsNullOrEmpty(organizationId))
                {
                    var managementClient = await auth0Management.getClient();
                    var organization = await managementClient.Organizations.GetAsync(organizationId);
                    OrganizationName = organization.DisplayName;
                }
            }
        }
    }
}

このコードは、読み取り専用のテキストボックスにOrganization名を表示するだけのページを定義します。Razorコンポーネントの冒頭で記述されているAuthorize属性に注目してください。このページへのアクセスを許可されたユーザーロールとして"admin"を指定しています。

次に、MyBlazorSaaS/Components/PagesフォルダにあるHome.razorファイルを開き、以下で強調表示されているマークアップを追加します。

@if (!string.IsNullOrWhiteSpace(OrganizationMessage)) {
    <div class="alert alert-success" role="alert">
        @OrganizationMessage
    </div>
}

<AuthorizeView Roles="admin">
    <Authorized>
        <div class="text-center">
            <p>Manage your Organization</p>
            <a href="Dashboard">Dashboard</a>
        </div>
    </Authorized>
</AuthorizeView>

AuthorizeViewコンポーネントは、adminロールを持つユーザーにのみダッシュボードへのリンクを表示します。

Blazorアプリでロールを利用可能にする

B2B SaaS Blazorアプリケーションでロールサポートを機能させるには、あと1つ小さなステップが欠けています。ユーザーのロールをIDトークンに追加することでPost Loginトリガーを処理するAuth0 Actionを作成する必要があります。

Blazorアプリケーションでロールを利用可能にする詳細については、こちらの記事をお読みください(英語)

基本的に、作成する必要があるAuth0 Actionには次のコードが含まれます。

exports.onExecutePostLogin = async (event, api) => {
  const roleClaim = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role';

  if (event.authorization) {
    api.idToken.setCustomClaim(roleClaim, event.authorization.roles);
  }
}

Actionをデプロイしログインフローに追加してください。Auth0 Actionsの紹介については、こちらの記事をお読みください

Organizationでのロールベースのアクセス制御をテストする

それではBlazorアプリケーションをテストして、ユーザーが割り当てられたロールに基づいてアクセスできることを確認しましょう。

Organization管理者としてテストする

アプリケーションを実行し、ホームページの右上隅にあるSubscribeリンクをクリックして新しいOrganizationを作成します。新しく作成されたユーザーにはadminロールが割り当てられるため、Auth0 Organizationが作成された後、ホームページは次の画像のようになります。

Blazor B2B SaaSアプリケーションのホームページ、管理者ユーザー向けの組織管理リンク付き

ページ中央にある"Manage your Organization"のリンクに注目してください。そのリンクに移動すると、次のページが表示されます。

Blazor B2B SaaSアプリケーションの管理者ダッシュボード、組織名を表示

現在のOrganizationの管理者として、アプリケーションのダッシュボードにアクセスできます。

Organizationメンバーとしてテストする

アプリケーションからログアウトし、Auth0ダッシュボードを使用してOrganizationにメンバーを追加し、メンバーロールを割り当てます。将来の記事では、アプリケーションのダッシュボードを通じてOrganizationにメンバーを追加する方法を学びます。

メンバーユーザーとしてBlazorアプリにログインすると、次のホームページが表示されます。

メンバーユーザー向けのBlazor B2B SaaSアプリケーションのホームページ、組織管理リンクなし

Organizationメンバーとして、アプリケーションダッシュボードへのアクセスリンクは表示されません。

Try out Auth0 authentication for free.

Get started →

まとめ

本記事ではAuth0 Organizationsを使用してB2B SaaS Blazorアプリケーションにロールベースのアクセス制御(RBAC)を設定する方法を順を追って説明しました。Auth0 Management APIを使用して"admin"や"member"のようなロールを作成し、それらをOrganizationに属するユーザーに対して割り当てる方法を学びました。

また、BlazorアプリのIDトークンにユーザーロールを追加するためのAuth0 Actionも設定しました。

最後に、Authorize属性とAuthorizeViewコンポーネントを使用して、これらのロールに基づいてルートとコンテンツを保護する方法を示しました。adminとmemberの両方のロールでテストし、RBACが機能していること、つまり承認されたユーザーのみがダッシュボードにアクセスできることを確認しました。

記事を通して実装されたサンプルプロジェクトの最終的なコードはこちらで確認できます。

将来の記事では、ダッシュボードに新規ユーザーを作成する機能を追加します。ご期待ください。