developers

Blazor B2B SaaSアプリケーションでセルフサブスクリプションモデルを有効にする方法

Auth0.NET SDKを使用してB2B SaaSアプリケーションでセルフサブスクリプションを有効にし、Organizationの作成プロセスを自動化する方法を学びます。

本記事は 2025年3月25日に更新された「Enable Self-Subscribing Model in Your Blazor B2B SaaS Application」を機械翻訳した記事です。

最近の記事では、Auth0 OrganizationsがB2B Blazor Webアプリケーションの構築にどのように役立つかを探りました。Auth0ダッシュボードを使用して顧客向けのOrganizationを作成および構成する方法を学びましたが、これは簡単であるものの、手動での介入が必要であり長期的には持続可能な方法ではありません。この記事では手動での作業を回避し、Auth0 Management APIを介してOrganizationの作成と構成を自動化する方法を学びます。この手法を用いると、B2B SaaSアプリケーションとしての成功がさらに促進されます。

Organizationの作成を自動化する理由

Auth0ダッシュボードを使用してB2B顧客向けのOrganizationを作成するのは非常に簡単ですが、いくつかの理由からスケーラブルではありません。

  • 新しいB2B顧客ごとに手動の作業が必要です。時間がかかるだけでなく、エラーが発生しやすくなります。
  • ビジネスが成長し、幸いにも多くの新しい顧客を獲得した場合、この作業は膨大になり、ボトルネックになるリスクがあります。新しい顧客が製品を試すためにOrganizationの構築を待っていることを想像してみてください。この状況は製品を紹介する最良の状態ではありません。
  • 現代のビジネスシナリオでは、顧客はセルフサービスを好みます。たとえば、SaaSアプリケーションの無料トライアルに登録し、必要に応じて連絡を取りながら、自律的に製品を評価することを好みます。
  • 顧客に対してセルフサービスでOrganizationの作成機能を提供することで、ビジネスの成功につながるProduct-Led Growth (PLG)の原則に沿うことができます。

それでは新しい顧客がB2B SaaSアプリケーションへのサブスクライブとOrganizationの作成をセルフサービスで開始できる機能を実装する方法を見ていきましょう。

Organization自動作成のシナリオ

SaaSとして公開されているB2B Blazorアプリケーションがあり、新しい顧客が自律的にサブスクライブして使用を開始できる機能を追加したいとします。たとえば、期間限定でSaaSアプリケーションを無料で使用できるようにしたり、機能の一部を永久に無料で使用できるようにしたりできます。

顧客が製品を評価するためにどのようなアプローチを取りたいかにかかわらず、アプリにサブスクライブすると、新しいAuth0 Organizationが作成され、そのアカウントがそのOrganizationに追加されます。

このシナリオでは、アプリケーションはAuth0との関連で2つの役割を果たします。

  • アプリケーションの役割: これはBlazorアプリケーションの通常の動作、つまり、アプリケーションが顧客のユーザーに機能を提供する役割です。この役割では、ユーザーがAuth0 Organizationに属するものとして認証されます。
  • オンボーディングの役割: これは、新しい顧客のサブスクリプションプロセス中にアプリケーションが果たす役割です。この段階では、新しいユーザー向けのOrganizationはまだ存在せず、Organizationのコンテキスト外で作成および認証されます。この役割では、アプリケーションはAuth0 Management APIを使用して新しいOrganizationを作成し、最終的に新しいユーザーをメンバーとして追加します。

アプリケーションの役割を果たすには、Regular Webアプリケーションとして登録し、Organizationsを使用するように構成する必要があります。

オンボーディングの役割を果たすには、Auth0ダッシュボードで新しいアプリケーションを登録し、Blazorアプリケーションにいくつかの変更を加える必要があります。これについては後で説明します。

この記事で説明する手順を開始するには、次のコマンドを使用してサンプルBlazorアプリケーションをダウンロードできます。

git clone --branch organization-manual --single-branch https://github.com/andychiare/MyBlazorSaaS

アプリケーションを登録および構成するには、添付のREADMEファイルの指示に従ってください。

以下のセクションでは、次のことを行います。

  • アプリケーションのオンボーディングの役割を有効にする。
  • 顧客のサブスクリプションプロセスを開始する。
  • 新しいサブスクライバー顧客に代わってAuth0 Organizationを作成する。

Leverage Auth0's authentication and authorization services in your .NET applications.

DOWNLOAD THE FREE EBOOK.NET Identity with Auth0

オンボーディングプロセスを有効にする

アプリケーションのオンボーディングの役割を有効にするには、Auth0テナントに新しいアプリケーションを登録します。その後、オンボーディングプロセスを構成するため、コードにいくつかの変更を加え、Auth0 Management APIとの対話を有効にします。

オンボーディング用にアプリケーションを登録する

まず、Auth0 ダッシュボードにアクセスし、次の図に示すようにRegular Webアプリケーションを登録します。

Create management API client

アプリケーションの登録後、そのDomain、Client ID、およびClient Secretをメモしておきます。次に、 Application URIs セクションまでスクロールし、Allowed Callback URLs フィールドにhttps://localhost:7187/onboarding/callbackを、Allowed Logout URLs フィールドにhttps://localhost:7187/を割り当てます。

最後に Save Changes をクリックして変更を確認します。

次に、アプリケーションの構成ページの APIs タブを選択し、Auth0 Management APIに対応するスイッチボタンを切り替えます。これでアプリケーションがそのAPIにアクセスできます。続けてAuth0 Management API項目の下の領域を展開します。

Authorize the management API client

ここでは、顧客のオンボーディングに必要な権限を選択する必要があります。具体的には、Organizationを作成および構成し、新しいユーザーをそれに追加する必要があります。この目的のために、次の権限を有効にします。

read:users, create:users, update:users, read:organizations, create:organizations, update:organizations, read:organization_members, create:organization_members, create:organization_connections

原則として、テナントで実行しようとする操作に厳密に必要な権限のみを有効にしてください。

Update ボタンをクリックして変更を適用することを忘れないでください。

オンボーディング用にアプリケーションを構成する

BlazorプロジェクトでMyBlazorSaaSフォルダにあるappsettings.jsonファイルを開き、以下に強調表示されているキーを追加します。

//MyBlazorSaaS/appsettings.json

{
 "Logging": {
 "LogLevel": {
 "Default": "Information",
 "Microsoft.AspNetCore": "Warning"
 }
 },
 "AllowedHosts": "*",
 "Auth0": {
 "Domain": "YOUR_DOMAIN",
 "ClientId": "YOUR_CLIENT_ID",
 // 👇 new settings
 "ManagementClientId": "YOUR_MANAGEMENT_CLIENT_ID",
 "ManagementClientSecret": "YOUR_MANAGEMENT_CLIENT_SECRET",
 "DefaultConnectionId": "YOUR_CONNECTION_ID"
 // 👆 new settings
 }
}

YOUR_MANAGEMENT_CLIENT_ID および YOUR_MANAGEMENT_CLIENT_SECRETを、Auth0に登録したばかりのアプリケーションの対応する値に置き換えます。

また、アプリケーションのOrganizationsのユーザーソースとして使用するConnectionのIDも必要です。Organizationsにユーザーを提供する方法を定義するためにさまざまな戦略を使用できますが、この記事では、デフォルトの Username-Password-Database connectionを使用します。したがって、Auth0ダッシュボードで、Authentication - Databaseをクリックし、次に Username-Password-Database を選択します。表示されるページで、次の図に示すように、接続名のすぐ下にある接続識別子を見つけます。

auth0-connection-identifier

YOUR_CONNECTION_IDをこの値に置き換えます。

オンボーディングの認証とログアウトを構成する

前述のように新しい顧客がアプリケーションにサブスクライブする際、サインアップできるようにする必要があります。ただし、現在、アプリケーションはOrganizationのコンテキストでユーザーを認証するように構成されています。新しいユーザーはどのOrganizationにも関連付けられていないため、Organizationのコンテキスト外でユーザーのサインアップと認証を処理するための別のフローが必要です。

この場合、ASP.NET Core認証の観点から見ると、新しい認証スキームを構成する必要があります。以下に強調表示されているコードは、MyBlazorSaaSフォルダのProgram.csファイルでこの新しい認証スキームを定義しています。

//MyBlazorSaaS/Program.cs


//...existing code...

builder.Services
    .AddAuth0WebAppAuthentication(options => {
 options.Domain = builder.Configuration["Auth0:Domain"];
 options.ClientId = builder.Configuration["Auth0:ClientId"];
 options.Scope = "openid profile email";
    });

// 👇 new code
builder.Services
    .AddAuth0WebAppAuthentication("OnboardingScheme", options => {
 options.Domain = builder.Configuration["Auth0:Domain"];
 options.ClientId = builder.Configuration["Auth0:ManagementClientId"];
 options.ClientSecret = builder.Configuration["Auth0:ManagementClientSecret"];
 options.Scope = "openid profile email";
 options.CookieAuthenticationScheme = "OnboardingCookieScheme";
 options.CallbackPath = "/onboarding/callback";
    });
// 👆 new code

//...existing code...

上記のコードスニペットでは、Organizationのコンテキストでユーザーを認証する既存の認証スキームを確認できます。この認証スキームでは「アプリケーションの役割」で利用される構成データを使用します。

新しいコードは、アプリケーションの「オンボーディングの役割」に関する新しいアプリケーション登録の構成データを使用します。この場合、この新しい認証スキームに"OnboardingScheme"という名前を割り当てました。さらに、Cookie認証スキームの名前と、Allowed Callback URLs フィールドに登録した値の相対パスである特定のコールバックパスを追加したことがわかります。これら2つの追加オプションは、デフォルト名を使用する他の認証スキームとの競合を防ぐために必要です。

新しい認証スキームを追加したので、この認証スキームからログアウトする必要もあります。Program.csファイルで、ログアウトエンドポイントの定義を見つけ、次のコードに強調表示されているステートメントを追加します。

//MyBlazorSaaS/Program.cs


//...existing code...

app.MapGet("/account/logout", async (HttpContext httpContext, string returnUrl = "/") =>
{
  var authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
          .WithRedirectUri(returnUrl)
          .Build();

  await httpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
  await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
  // 👇 new code
  await httpContext.SignOutAsync("OnboardingScheme", authenticationProperties);
  await httpContext.SignOutAsync("OnboardingCookieScheme");
  // 👆 new code
});

//...existing code...

既存のログアウトステートメントと同様に、これらの新しいステートメントは、オンボーディングスキームに関連付けられた認証済みセッションを閉じます。

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

Install the Nugget packageAuth0 Templates for .NET

サブスクリプションプロセスを開始する

オンボーディングの構成が完了した後、顧客がアプリケーションにサブスクライブして新しいOrganizationを作成できるようにする手順を実装しましょう。

MyBlazorSaaS/Components/LayoutフォルダにあるMainLayout.razorコンポーネントを開き、「About」マークアップを以下に強調表示されているマークアップに置き換えます。

<!-- MyBlazorSaaS/Components/Layout/MainLayout.razor -->

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <Login />
            <!-- 👇 new code -->
            <a href="Onboarding/Signup">Subscribe</a>
            <!-- 👆 new code -->
        </div>

        <article class="content px-4">
 @Body
        </article>
    </main>
</div>

<!-- ...existing code --->

このコードは、Blazorアプリケーションの右上隅に Subscribe メニュー項目を追加します。その結果は次のように表示されます。

Blazor SaaS app's home page with subscribe menu item

Subscribe メニュー項目がOnboarding/Signup URLを指していることがわかります。したがって、このリクエストを処理するRazorコンポーネントを実装しましょう。

MyBlazorSaaS/Components/Pagesフォルダの下にOnboardingフォルダを作成します。そのOnboardingフォルダにSignup.razorファイルを作成し、次のコードを記述します。

<!-- MyBlazorSaaS/Components/Pages/Onboarding/Signup.razor -->
  
@page "/Onboarding/Signup"
@rendermode InteractiveServer
@using System.Web
@inject NavigationManager NavigationManager

<h1>Try MyBlazorSaaS for Free</h1>
<p>
 Enter your email address to sign up and create a new organization for you and your collaborators.
</p>

<div>
  <div class="form-group row">
    <label for="email" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-6">
      <InputText 
 type="email"
 @bind-Value="Email"
 placeholder="name@example.com"
        class="form-control"
 id="email"
 required/>
 </div>
 </div>
  <button class="btn btn-primary" @onclick="StartSignup">Get Started</button>
</div>

@code {
  private string Email = "";

  private void StartSignup()
  {
 NavigationManager.NavigateTo($"/account/signup?login_hint={HttpUtility.UrlEncode(Email)}");
  }
}

このコードは、オンボーディングプロセスを開始するために顧客のメールアドレスを要求します。

Customer signup form

顧客がメールアドレスを入力して Get Started ボタンをクリックすると、メールアドレスをパラメータとして/account/signupエンドポイントにリダイレクトされます。

サインアップリクエストを処理するには、MyBlazorSaaSフォルダにあるProgram.csファイルを開き、次のコードスニペットに強調表示されているコードを追加します。

// MyBlazorSaaS/Program.cs

//...existing code..

app.MapGet("/account/logout", async (HttpContext httpContext, string returnUrl = "/") =>
{
  //...existing code..
});

// 👇 new code
app.MapGet("/account/signup", async (HttpContext httpContext, string login_hint) =>
{
  var authenticationProperties = new LoginAuthenticationPropertiesBuilder()
          .WithRedirectUri("/onboarding/createOrganization")
          .WithParameter("screen_hint", "signup")
          .WithParameter("login_hint", login_hint)
          .Build();

  await httpContext.ChallengeAsync("OnboardingScheme", authenticationProperties);
});
// 👆 new code

app.MapGet("/api/internalData", () =>
{
  //...existing code..
}

//...existing code..

ここでは/account/signup URLへのリクエストを処理するための最小限のAPIエンドポイントを実装します。このエンドポイントは、顧客のメールアドレスを含むlogin_hintパラメータを受け取り、認証リクエストを構築します。認証リクエストが、Auth0に通常のログインフォームではなくサインアップフォームを表示するよう指示するために、Universal Loginのscreen_hintパラメータ を使用していることがわかります。また、login_hintパラメータをあわせて渡し、ユーザー名フィールドを入力しておきます。最後に、サインアッププロセスが完了した後、ユーザーをリダイレクトするURL/onboarding/createOrganizationを指定します。

サインアッププロセスを開始するために、httpContext.ChallengeAsync()メソッドを呼び出します。この場合、ユーザーはOrganizationのコンテキスト外で作成および認証される必要があるため、"OnboardingScheme"認証スキームが指定されていることに注意してください。

実際のシナリオでは、Organizationの作成に進む前に、ユーザーのメールアドレスを確認する必要があります。デフォルトでは、Auth0はユーザーがサインアップすると検証リンクを含むメールを送信しますが、アクションを実行できるようにする前にメールが検証済みかどうかを確認するのはユーザー次第です。ここでは簡略化のためにメール検証を省略していますが、顧客オンボーディングフローにメール検証ステップを追加する方法については、この記事を参照してください。

Auth0 Organizationを作成する

顧客がAuth0でサインアッププロセスを開始すると、アカウントが作成され、Universal Loginページを通じて認証されます。ちなみに、このページはデザインニーズに合わせてカスタマイズできます。サインアップリクエストを構成したため、アカウント作成後、Organizationを作成できるページにリダイレクトされます。

Management APIクライアントを有効にする

顧客のOrganizationを作成するには、BlazorアプリケーションがAuth0 Management APIを呼び出す必要があります。このAPIを使用すると、Auth0ダッシュボードで実行できるすべての操作をコードから実行できます。

Auth0 Management APIを使用するために、Auth0.NET SDKを使用します。Auth0.NET SDKを使用すると、管理クライアント と呼ぶManagement APIのクライアントを作成できます。Auth0.NET SDKは2つのパッケージで構成されています。

  • Authentication APIを使用してログイン、ログアウト、サインアップを処理できる Authentication API パッケージ。
  • すべてのテナント管理操作を処理できる Management API パッケージ。

.NET で利用可能なさまざまな Auth0 SDKの詳細については、この記事を参照してください。

まず、次のコマンドを使用して、MyBlazorSaaSフォルダでホストされているBlazorサーバープロジェクトに両方のパッケージをインストールしましょう。

dotnet add package Auth0.ManagementApi
dotnet add package Auth0.AuthenticationApi

また、顧客が提供するOrganization名についてわかりやすいSlugを作成する必要があります。サンプルプロジェクトでは、Slugifyパッケージを使用しますが、必要に応じて別のソリューションを使用してもかまいません。Slugifyをプロジェクトにインストールするには、次のコマンドを実行します。

dotnet add package Slugify.Core

Management APIクライアントを構築する

BlazorアプリケーションがAuth0 Management APIを呼び出すことができるようにするパッケージを追加した後、管理クライアントを作成しましょう。Auth0 Management APIを呼び出す必要があるたびに管理クライアントの新しいインスタンスを作成する代わりに、コードの重複を避けるだけでなく、時間とリソースを節約するのに役立つシングルトンを構築できます。この目的のために、MyBlazorSaaSフォルダにAuth0Management.csという名前の新しいファイルを追加し、次のコードを記述します。

using Auth0.ManagementApi;
using Auth0.AuthenticationApi;
using Auth0.AuthenticationApi.Models;

interface IAuth0Management
{
 Task<ManagementApiClient> getClient();
}

public class Auth0Management: IAuth0Management
{
  private string _domain = "";
  private string _clientId = "";
  private string _clientSecret = "";
  private ManagementApiClient managementClient = null;

  public Auth0Management(string domain, string clientId, string clientSecret)
  {
 _domain = domain;
 _clientId = clientId;
 _clientSecret = clientSecret;
  }

  public async Task<ManagementApiClient> getClient()
  {
    if (managementClient is null)
    {
      var authClient = new AuthenticationApiClient(_domain);
      var token = await authClient.GetTokenAsync(new ClientCredentialsTokenRequest
      {
 Audience = $"https://{_domain}/api/v2/",
 ClientId = _clientId,
 ClientSecret = _clientSecret
      });
 managementClient = new ManagementApiClient(token.AccessToken, _domain);   
    }
    return managementClient;
  }
}

ここでは、1つのメソッドgetClient()を持つインターフェースIAuth0Managementの定義を確認できます。

このインターフェースの下には、このインターフェースの実装クラスAuth0Managementがあります。このクラスのコンストラクタは、管理クライアントの構築に必要な3つのパラメータを受け取り、プライベート変数に格納します。

getClient()メソッドは、最初に管理クライアントのインスタンスが既に存在するかどうかを確認します。存在する場合は、既存のインスタンスが返されます。それ以外の場合は、認証クライアント authClientのインスタンスを作成し、それを使用してAuth0 Management APIのアクセストークンを取得します。この場合、Client Credentialフローを使用していることに注意してください。ここで渡しているパラメータは、オンボーディングの役割を果たすアプリケーションの登録から取得したものです。

次に、アクセストークンとAuth0ドメインを渡して管理クライアントのインスタンスを作成します。

管理クライアントをアプリケーションで使用できるようにするには、シングルトンサービスとして登録する必要があります。MyBlazorSaaSフォルダにあるProgram.csを開き、次のコードを追加します。

//...existing code..

// 👇 new code
builder.Services.AddSingleton<IAuth0Management>(sp => 
  new Auth0Management(
 builder.Configuration["Auth0:Domain"],
 builder.Configuration["Auth0:ManagementClientId"],
 builder.Configuration["Auth0:ManagementClientSecret"]
  )
);
// 👆 new code

var app = builder.Build();

//...existing code..

管理クライアントを構成するためにサービスコンストラクタへ渡した値に注意してください。今後、コードが管理クライアントのインスタンスを必要とする場合はいつでも、依存性注入メカニズムを利用できます。

Organizationの作成を開始する

ここからはユーザーにOrganizationの名前を尋ね、その情報からOraganizationを作成するページの実装を開始します。

MyBlazorSaaS/Components/Pages/Onboardingフォルダに、CreateOrganization.razorという名前のRazorコンポーネントを作成し、次のコードを記述します。

<!-- MyBlazorSaaS/Components/Pages/Onboarding/CreateOrganization.razor -->
  
@page "/Onboarding/CreateOrganization"
@rendermode InteractiveServer

@using System.Security.Claims
@using Auth0.AuthenticationApi;
@using Auth0.ManagementApi;
@using Auth0.AuthenticationApi.Models;
@using Auth0.ManagementApi.Models
@using System.Web
@using Slugify
@using Microsoft.Extensions.Configuration

@inject NavigationManager NavigationManager
@inject IConfiguration Configuration
@inject IAuth0Management auth0Management

<h1>Create an account</h1>
<p>
Enter your organization name to create an account.
</p>

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

  <div class="form-group row">
    <label for="organization" class="col-sm-4 col-form-label">Organization name</label>
    <div class="col-sm-6">
      <InputText 
 @bind-Value="OrganizationName"
 placeholder="Acme Corp."
 id="organization"
        class="form-control"
 required/>
 </div>
 </div>

  <button class="btn btn-primary" @onclick="StartCreateOrganization">Create Organization</button>
</div>

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

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

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

      if (state.User?.Identity?.IsAuthenticated??false)
      {
 Email = state.User?.FindFirst(c => c.Type == ClaimTypes.Email)?.Value??String.Empty;
 UserId = state.User?.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value??String.Empty;
      } else {
 NavigationManager.NavigateTo("/Onboarding/Signup");
      }
    }
  }

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

      try {
        //Get management client
        var managementClient = await auth0Management.getClient();

        //Create organization
 organization = await managementClient.Organizations.CreateAsync(
          new OrganizationCreateRequest() { 
 Name = new SlugHelper().GenerateSlug(OrganizationName),
 DisplayName = OrganizationName,
 EnabledConnections = [new OrganizationConnectionCreateRequest() { ConnectionId = Configuration["Auth0:DefaultConnectionId"]}]
          }
        );

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

        // Redirect to the Organization login
 NavigationManager.NavigateTo($"/Account/Login?organizationId={organization.Id}");
      } catch (Exception ex) {
 Message = "Failed to create an organization.";
      }
    } else {
 Message = "Organization name is required.";
    }    
  }
}

このコードには多くの記述があります。1つずつ分析していきましょう。

まず、マークアップは次のようにレンダリングされるフォームを定義します。

create-organization-form

OnInitializedAsync()メソッドは、ユーザーがこのページへアクセスしたときに実行されるチェックを定義します。

//...existing code...

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

      if (state.User?.Identity?.IsAuthenticated??false)
      {
 Email = state.User?.FindFirst(c => c.Type == ClaimTypes.Email)?.Value??String.Empty;
 UserId = state.User?.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value??String.Empty;
      } else {
 NavigationManager.NavigateTo("/Onboarding/Signup");
      }
    }
  }

//...existing code...

ここではユーザーが認証されていることを確認します。この場合、Auth0がIDトークンを通じて提供するクレームから、ユーザーのメールアドレスとIDを抽出します。それ以外の場合、ユーザーはサインアップフォームにリダイレクトされます。

StartCreateOrganization()メソッドは、ユーザーが Create Organization ボタンをクリックすると呼び出されます。その中で実装されている主な手順について説明しましょう。

管理クライアントを取得する

最初の手順では、管理クライアントのインスタンスを取得します。

//...existing code...

//Get management client
var managementClient = await auth0Management.getClient();

//...existing code...

ページの先頭にある@inject IAuth0Management auth0Managementステートメントを通じて注入されたauth0ManagementインスタンスのgetClient()メソッドを呼び出します。

Organizationを作成する

次のステップでは管理クライアントを使用して新しいOrganizationを作成します。

//...existing code...

organization = await managementClient.Organizations.CreateAsync(
  new OrganizationCreateRequest() { 
 Name = new SlugHelper().GenerateSlug(OrganizationName),
 DisplayName = OrganizationName,
 EnabledConnections = [new OrganizationConnectionCreateRequest() 
      { ConnectionId = Configuration["Auth0:DefaultConnectionId"]}]
  }
);

//...existing code...

managementClient.Organizations.CreateAsync()メソッドを使用し、OrganizationCreateRequestクラスのインスタンスを渡します。このインスタンスにはOrganizationを作成するための最小限のパラメータが含まれています。

  • Name: Organizationの一意の論理識別子であり、変数名の規則(スペースなし、特殊文字なしなど)に従います。この識別子は、ユーザーが提供したOrganization名からSlugを生成し設定します。
  • DisplayName: Organizationの示すわかりやすいな名前です。
  • EnabledConnections: Organizationによって有効になっているConnectionsのリストです。この例では、この記事の冒頭で説明したデフォルトの Username-Password-Database connectionを追加しています。

Organizationにメンバーを追加する

Organizationを作成した後、次のコードを使用して現在のユーザーをそのメンバーとして追加します。

//...existing code...

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

//...existing code...

ここではmanagementClient.Organizations.AddMembersAsync()メソッドを呼び出し、新しく作成されたOrganizationのIDと現在のユーザーのIDをOrganizationAddMembersRequestインスタンスの形式で渡します。

最後のステップでは作成したばかりのOrganizationのコンテキストでログインするようにユーザーをリダイレクトします。

//...existing code...

NavigationManager.NavigateTo($"/Account/Login?organizationId={organization.Id}");

//...existing code...

このリダイレクトではユーザーはすでに認証されているため、ログインフォームは表示されません。ただし、指定されたOrganizationのコンテキストでサイレント認証されます。

最後のポイント

フローを完了し、新しいOrganizationへログインしたというフィードバックをユーザーに提供するため、ホームページにOrganization名を含むメッセージを追加しましょう。MyBlazorSaaS/Components/PagesフォルダにあるHome.razorページを開き、以下に強調表示されているコードを追加します。

<!-- MyBlazorSaaS/Components/Pages/Home.razor -->

@page "/"
@inject IAuth0Management auth0Management
<!-- 👆 new code -->

<PageTitle>Home</PageTitle>

<p>
MyBlazorSaaS is a reference B2B SaaS application built using Blazor and Auth0.
</p>

<!-- 👇 new code -->
@if (!string.IsNullOrWhiteSpace(OrganizationMessage)) {
  <div class="alert alert-success" role="alert">
 @OrganizationMessage
 </div>
}
<!-- 👆 new code -->
  
<!-- ...existing markup... -->

<!-- 👇 new code -->
@code {
  [CascadingParameter]
  private Task<AuthenticationState>? authenticationState { get; set; }
  private string OrganizationMessage = "";

  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);
 OrganizationMessage = $"You are authenticated in the Organization '{organization.DisplayName}'";
        }
      }
    }
  }
}
<!-- 👆 new code -->

追加したマークアップは、OrganizationMessage変数が空でない文字列値を持つ場合に警告メッセージを表示します。ファイルの最後に追加したコードブロックは、ユーザーが認証されているかどうかを確認します。認証されている場合、ユーザーのクレームからOrganization IDを取得し、管理クライアントインスタンスを使用して現在のOrganizationの表示名を取得します。

最終的な結果は次のようになります。

Login to Auth0 Organization

Auth0 Organizationsの力を活用して、新しい顧客がセルフサービスでサービスにサブスクライブできる機能を最新のBlazorアプリケーションを構築できました。

Try out Auth0 authentication for free.

Get started →

まとめ

これでB2B SaaSアプリケーションに新しい顧客が登録するための必要な要素が揃いました。

今回のBlog記事ではアプリケーションがAuth0コンテキストで「通常のアプリケーションの役割」と、「新しい顧客向けのオンボーディングの役割」という2つの役割を果たしていることを確認しました。

「オンボーディングの役割」では、アプリケーションはAuth0 Management APIにアクセスする権限が必要であり、新しい顧客がOrganizationのコンテキスト外で登録できるようにする必要があります。登録されると、アプリケーションはManagement APIを使用して新しい顧客向けの新しいOrganizationを作成し、その顧客をメンバーとして追加します。

この時点で、顧客のオンボーディングは完了です。新しいOrganizationで認証し、アプリケーションの機能を自由に探索できます。