本記事は 2025年2月18日に更新された「Add Auth0 Organizations to Your B2B Blazor Web App」を機械翻訳した記事です。
B2B SaaSを構築する際、セキュリティを維持しながらより良いリソース配分を実現するためにマルチテナントが必要です。Auth0 Organizationsは、高度な柔軟性でB2Bアプリケーションを構築できる一連の機能を提供します。これらの機能を使用して、Blazorを使用した.NETプラットフォーム上にB2B SaaSを構築する方法を探りましょう。
この記事では、サンプルプロジェクトを構築するための参照フレームワークとしてBlazorを使用していますが、ASP.NET Core MVCや Razor Pagesなど、他のサーバーサイドASP.NET Coreフレームワークでも同じアプローチを使用できます。
Organizationsを使用する理由
Auth0を使用してアプリケーションを保護する場合、リソースと専用のユーザーを分離するためにAuth0テナントを設定します。ユーザーベースがそれ以上の構造化を必要としない場合はこれで十分です。ユーザーグループをさらに分離する必要がある場合、異なる認証オプション、異なるロールセット、または異なるブランディングを使用してアプリケーションにアクセスできるユーザーグループを作成したいとします。これはB2B SaaSの典型的なシナリオであり、ビジネス顧客にアプリケーションへのアクセスを提供します。ビジネス顧客は独自のユーザーを持っており、同じアプリケーションを使用しながら、セキュリティとカスタマイズのためにこれらのユーザーを分離する必要があります。
ここでAuth0 Organizationsが役立ちます。
次の図に示すように、大まかに言うとAuth0 Organizationsは、Auth0テナント内のユーザーグループを構造化する方法と見なすことができます。
B2Bシナリオでは、アプリケーションは1つのAuth0テナントに関連付けられ、顧客ごとに1つの複数のOrganizationsをサポートします。これらのOrganizationsを使用すると、ユーザーを分離し、顧客ごとにアプリケーションをカスタマイズできます。
これがどのように機能するかを見てみましょう。
サンプルプロジェクト
この記事全体を通して、Auth0認証を備えた基本的なBlazorアプリケーションを使用します。Auth0 Organizationsのサポートを追加し、Organizationコンテキストでユーザーを追加および認証する方法を学びます。
この記事の目的のために、次の制約を持つB2Bシナリオに焦点を当てます。
- 顧客向けにAuth0 Organizationを設定します。
- ユーザーは自律的にサインアップできません。
- Organizationのメンバーのみがアプリケーションにアクセスできます。
- 顧客に代わってOrganizationのメンバーを作成します。
スターターアプリケーションは、次の2つの方法で構築できます。
- Auth0 Templates for .NETを使用する。
- ゼロから構築する。
Auth0 Templates for .NETを使用する
Auth0 Templates for .NET、具体的にはBlazor Web Applicationテンプレートを利用して、スターターアプリケーションを構築できます。NuGetパッケージをインストールする手順に従い、.NET CLI、Visual Studio、またはJetBrains Riderを介してBlazor Web Appテンプレートを使用してください。
.NET CLIを使用しており、マシンにAuth0 CLIがインストールされ、Auth0テナントにログインしている場合は、次のコマンドを実行するだけで自動登録機能を利用できます。
dotnet new auth0blazor -o MyBlazorSaaS
このコマンドは、Auth0認証が埋め込まれたすぐに実行できるBlazorアプリケーションを
MyBlazorSaaS
フォルダに作成します。それ以外の場合、Auth0 CLIがインストールされていない場合、またはVisual StudioやRiderでテンプレートを使用する場合は、Auth0にアプリを登録し、
appsettings.json
ファイルを手動で構成する必要があります。アプリケーションをゼロから構築する
スターターアプリケーションをゼロから構築する場合は、このドキュメントを読んで、基本的なBlazor Web AppにAuth0認証を追加する方法を学んでください。
スターターアプリケーションを構築および構成するためにどのようなアプローチを使用したとしても、すべてが期待どおりに機能することを確認するために実行してください。
<include src="ebook-ads/dotnetIdentity" />
新しいOrganizationを設定する
さて、あなたは素晴らしいSaaSアプリケーションをAcme Inc.という会社に販売したと仮定します。他の顧客のユーザーに干渉することなくアプリケーションを使用できるように、彼らのためにAuth0 Organizationを作成する必要があります。
新しいOrganizationを作成する
Auth0ダッシュボードに移動し、左側のメニューのOrganizationsを選択し、次に Create Organization ボタンをクリックします。次の図に示すように、新しいOrganizationの名前と表示名を入力するように求められます。
上の図では、作成しているOrganizationの名前として「acme」、表示名として「Acme Inc.」が表示されています。Add Organization ボタンをクリックすると、新しい顧客のOrganizationの構成ページに移動します。
おめでとうございます、顧客向けの基本的なOrganizationを作成しました。
Organizationの構成ページでは、顧客のOrganizationを管理およびカスタマイズできます。これについては後で使用しますが、利用可能なすべての機能を探るわけではありません。ドキュメントを読んで、Organization を構成する方法を学んでください。
Organizationsページに戻ると、新しいOrganizationがリストに表示されていることがわかります。
Identifier列に注目してください。この値を使用すると、コード内でこのOrganizationを識別できます。
OrganizationのConnectionsを有効にする
次に、Organizationの表示名(この例では「Acme Inc.」)に関連付けられたリンクをクリックしてOrganizationページを再度開き、Connectionsタブに移動します。ここでは、このOrganizationのコンテキストでユーザーがアプリケーションにログインする方法を決定する Connections を追加できます。
デフォルトでは、Connectionは構成されていません。したがって、Enable Connections ボタンをクリックし、有効にするConnectionを選択します。組み込みの
Username-Password-Authentication
接続のみを有効にするか、ソーシャル接続を追加できます。これは、顧客がユーザーにアプリケーションへのアクセスをどのように許可したいかによって異なります。Username-Password-Authentication
を選択し、Enable Connectionボタンをクリックします。テナントに対してすでに構成されているConnectionのみを有効にできることに注意してください。ここで有効にしたい接続が表示されない場合は、ダッシュボードのAuthenticationメニュー に移動して、最初に構成してください。
Connectionを選択して有効にすると、このOrganizationのコンテキストでの接続構成ページが表示され、ユーザーがOrganizationに参加する方法を定義できます。この選択は、構築したアプリケーションの種類と、ビジネス顧客がユーザーをどのように管理したいかによって厳密に異なります。この記事の目的のために、アプリケーションは顧客に誰がアクセスできるかを制御させることを意図していると仮定します。つまり、ユーザーはアプリケーションに自律的にサインアップできません。このシナリオでは
Username-Password-Authentication
Connectionのデフォルト構成のままで利用できます。ユーザーはアプリケーションにサインアップできません。Organizationの管理者のみがユーザーをOrganizationに追加できます。アプリケーションのOrganizationの動作を構成する
顧客のOrganizationが構成されたら、Organizationを介してユーザーがアプリケーションにアクセスする方法を定義する必要があります。Applications > Applicationsメニューに移動し、アプリケーションを選択します。構成ページで、Organizationsタブを選択します。次のようなページが表示されます。
このページでは、Organizationsのコンテキストでアプリケーションにログインできるユーザーの種類と、有効にするログインフローを定義します。上の図は、この記事の目的のために適用する設定を示しています。Organizationsのログインフローの詳細については、ドキュメントをお読みください。
上記の制約に基づいて、Organizationのメンバーのみがアプリケーションにログインできます。したがって、ユーザーの種類としてBusiness Usersを選択します。また、通常のログインページを表示したいので、ログインフローとしてPrompt for Credentialsを選択します。
これでOrganizationの構成は完了です。
ユーザー招待を管理する
次のステップは、新しく作成したOrganizationにユーザーを追加することです。アプリケーションで使用したい特定の戦略に応じて、これを実現する方法はいくつかあります。以前定義した制約に基づいて、Organizationへのサインアップをユーザーが自分自身ではできないようにします。この場合はAuth0テナントにユーザーを追加し、Organizationのメンバーにします。または、メールでOrganizationへの参加を招待できます。この場合、ユーザーはOrganizationに参加するためのリンクが記載されたメールを受け取ります。ユーザーがリンクをクリックして招待を受け入れると、自動的にOrganizationに追加されます。
招待フローを実装する方法を探りましょう。
招待リンクを構成する
まず、アプリケーションのルートに一致し、招待を処理するURLを定義する必要があります。
<BASE_URL>/account/invitation
をその招待URLとしましょう。ここで、<BASE_URL>
はアプリケーションのベースURLです。Auth0ダッシュボードのApplicationsページに移動し、アプリケーションを選択し、Settingsタブで、Application URIsセクションまでスクロールダウンします。ここで、次の図に示すように、招待リンクをApplication Login URIフィールドに追加します。<div className="alert alert-info alert-icon"> <i className="icon-budicon-500"></i> <strong>
<BASE_URL>
は公開されているアクセス可能なURLである必要があります。localhost
をここで使用できません。<br/>
テスト目的のため、hosts
ファイルでlocalhostにドメイン名を追加できます。
</strong>
</div>Save Changesボタンをクリックして構成を完了します。
招待を処理する
さて、ユーザーがメールで受信した招待を受け入れ、招待リンクをクリックすると、受信リクエストを処理する必要があります。Auth0によって生成された招待URLには、招待インスタンスのIDである
invitation
と、ユーザーが招待されたOrganizationのIDであるorganization
の2つのパラメータがあります。このリクエストを処理するエンドポイントをBlazorアプリケーションに実装しましょう。MyBlazorSaaS
フォルダに移動し、次のハイライトされたコードを追加してProgram.cs
ファイルを編集します。// MyBlazorSaaS/Program.cs // ...existing code... // 👇 new code app.MapGet("/account/invitation", async (HttpContext httpContext, string organization, string invitation) => { var authenticationProperties = new LoginAuthenticationPropertiesBuilder() .WithOrganization(organization) .WithInvitation(invitation) .Build(); await httpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties); }); // 👆 new code app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(MyBlazorSaaS.Client._Imports).Assembly); app.Run();
このコードは、Auth0 ASP.NET Core Authentication SDKを利用し、招待を使用してOrganization内でログインプロセスを開始します。
Universal Loginページとユーザーが受信するメールを調整しユーザー招待のエクスペリエンスをカスタマイズできます。詳細については、こちらをお読みください。
これでアプリケーションはユーザーが受け入れた招待を処理する準備ができました。
ユーザー招待を送信する
顧客のOrganizationにユーザーを招待するには、ダッシュボードのOrganizationsメニュー項目 に移動し、ユーザーを追加するOrganizationを選択します。Organizationページで、Invitationsタブを選択し、Invite Membersボタンをクリックします。
開いたページでアプリケーションを選択し、次の図に示すように招待するユーザーのメールアドレスを追加します。
Auth0ダッシュボードから一度に最大5人のユーザーを招待できます。オプションで、ユーザーが招待を受け入れる接続と、ユーザーに割り当てられるロールを定義できます。これらのオプションはこの記事の範囲外です。
招待フォームに記入し、Send Invitesボタンをクリックします。
その結果、招待リンクを記載したメールがユーザーに送信されます。また、招待リストページに新しく送信された招待がPendingステータスで表示されます。
ユーザーがメールで受信した招待URLをクリックすると、次の例に示すように、顧客のOrganizationに参加するためのサインアップフォームにリダイレクトされます。
ユーザーがパスワードを選択してプロファイルを作成すると、顧客のOrganizationのコンテキストでアプリケーションに自動的にログインします。
Organization内のユーザーを認証する
前述のとおり、ログインフローはいくつかの異なる方法で構成できます。この記事の目的のために、Prompt for Credentialsフローを適用しました。これは、次回Universal Loginページを介して認証することを意味します。
このシナリオでは、アプリケーションがユーザーを認証する前にユーザーのOrganizationを認識しているか、認識していないかの2つのケースを考慮する必要があります。両方のケースを分析してみましょう。
Organizationを認識せずにユーザーを認証する
アプリケーションがユーザーを認証する前にユーザーのOrganizationを認識していないと仮定します。この場合、ユーザーを認証するコードは変更されません。
// MyBlazorSaaS/Program.cs //...existing code... app.MapGet("/account/login", async (HttpContext httpContext, string redirectUri = "/") => { var authenticationProperties = new LoginAuthenticationPropertiesBuilder() .WithRedirectUri(redirectUri) .Build(); await httpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties); }); //...existing code...
Auth0はユーザーがどのOrganizationに属しているかを把握していないため、テナントのUniversal Loginページがユーザーに表示されます。つまり、ユーザーはOrganization用にカスタマイズした可能性のあるログインページを表示しません。認証後、Auth0はユーザーがどのOrganizationのメンバーであるかを知り、この情報をIDトークンおよびアクセストークンの
org_id
クレームに含めます。このシナリオでは、アプリケーションがOrganization IDをAuth0に渡さないため、セキュリティ上の理由から、IDトークンおよびアクセストークンで受信した
org_id
クレームの値を検証する必要があります。Auth0から受信したトークン内の
org_id
クレームを検証する方法の例を次に示します。// MyBlazorSaaS/Program.cs //...existing code... builder.Services .AddAuth0WebAppAuthentication(options => { options.Domain = builder.Configuration["Auth0:Domain"]; options.ClientId = builder.Configuration["Auth0:ClientId"]; // 👇 new code options.OpenIdConnectEvents = new OpenIdConnectEvents { OnTokenValidated = (context) => { var organizationClaimValue = context.SecurityToken.Claims.SingleOrDefault(claim => claim.Type == "org_id")?.Value; var expectedOrganizationIds = new List<string> {"org_123", "org_456>"}; if (!string.IsNullOrEmpty(organizationClaimValue) && !expectedOrganizationIds.Contains(organizationClaimValue)) { context.Fail("Unexpected org_id claim detected."); } return Task.CompletedTask; } }; // 👆 new code }); //...existing code...
TokenValidated
イベントのハンドラーを追加しました。このハンドラーは、org_id
クレームの値がOrganization IDのリストexpectedOrganizationIds
に含まれているかどうかを確認します。含まれていない場合は、例外が発生します。上記の例では、
expectedOrganizationIds
リストの内容はコードに埋め込まれています。実際のシナリオでは、このリストの内容はデータベースやAPIなどの外部ソースから取得されます。Organizationを認識してユーザーを認証する
アプリケーションがユーザーを認証する前にOrganization IDを認識している場合、カスタマイズされたログインページが表示されるように、コードはOrganization IDをAuth0に送信できます。Organization IDをAuth0に渡すには、次のハイライトされたコードのように、コードに1行追加するだけです。
// MyBlazorSaaS/Program.cs //...existing code... app.MapGet("/account/login", async (HttpContext httpContext, string redirectUri = "/") => { var authenticationProperties = new LoginAuthenticationPropertiesBuilder() .WithRedirectUri(redirectUri) .WithOrganization("<ORG_ID>") //👈 new code .Build(); await httpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties); }); //...existing code...
上記のコードでは、
WithOrganization()
メソッドはOrganization IDを文字列として取得し、Auth0に送信されるパラメータとして含めます。ユーザーを認証する前にアプリケーションがOrganization IDをどのように取得できるかは、この記事の範囲外です。
この場合、SDKが自動的に検証してくれるため、Organization IDを明示的に検証する必要はありません。
現在のユーザーのOrganizationを取得する
シナリオに関係なく、Auth0から受信するIDトークンとアクセストークンには、ユーザーが属するOrganizationのIDが含まれます。この値を使用して、この特定のOrganization用にアプリケーションをカスタマイズできます。
この値は、前述のトークン検証ステップでトークンから直接抽出できます。
var organizationClaimValue = context.SecurityToken.Claims.SingleOrDefault(claim => claim.Type == "org_id")?.Value;
いずれにせよ、AuthenticationStateの現在のユーザーのClaimsPrincipalオブジェクトから、現在のユーザーに関連付けられたOrganization IDを抽出できます。
private Task<AuthenticationState>? authenticationState { get; set; } ... if (authenticationState is not null) { var state = await authenticationState; var orgId = state.User.Claims.SingleOrDefault(claim => claim.Type == "org_id")?.Value; }
<include src="SignupCTA" text="Try out Auth0 authentication for free." linkText="Get started →" />
まとめ
Organizationsの調査という長い道のりでしたが、それだけの価値はありました。
今回のBlogでは顧客のユーザーを認証するための新しいOrganizationを作成し、構成する方法を学びました。その後、アプリケーションのユーザーの種類とログインフローを定義し、ユーザー招待を構成および送信する方法を学びました。さらに、アプリケーション側で、それらのユーザー招待の受け入れを処理するようにコードを変更しました。最後に、ユーザーがアクセスしたいOrganizationを事前に把握している場合と把握していない場合のどちらでも、Organizationユーザーを認証する方法と、現在のユーザーのOrganization IDを取得してユーザーエクスペリエンスをカスタマイズする方法を確認しました。
もちろん、この記事で行ったAuth0 Organizationsの使用は最小限であり、手動で設定していた部分が多くを占めています。ぜひこの記事を読んで、コードを介してOrganizationsを作成し、顧客がB2B SaaSアプリケーションに自己登録する方法を学んでください。
Organizationsとユーザーの管理をより簡単かつ自動化するために、まだ多くのことを探求する必要があります。今後の記事のシリーズで、これらを一緒に探求していきます。ぜひご期待ください。
About the author
Andrea Chiarelli
Principal Developer Advocate
I have over 20 years of experience as a software engineer and technical author. Throughout my career, I've used several programming languages and technologies for the projects I was involved in, ranging from C# to JavaScript, ASP.NET to Node.js, Angular to React, SOAP to REST APIs, etc.
In the last few years, I've been focusing on simplifying the developer experience with Identity and related topics, especially in the .NET ecosystem.