🪪

ASP.NET Core Identity + Finbuckle.MultiTenantでマルチテナント認証認可を実装する

に公開

はじめに

SaaSアプリケーションの開発において、マルチテナント機能が要件となることがあります。本記事では、ASP.NET Core IdentityとFinbuckle.MultiTenantを組み合わせて、マルチテナント認証認可を実現してみたので解説します。

Finbuckle.MultiTenantは、.NET Foundationが支援するオープンソースのマルチテナンシーミドルウェアライブラリで、テナント解決、テナント別の動作設定、データ分離を簡潔に実装できる強力なツールです。最新バージョンでは.NET 8/9にも対応しています。

なぜFinbuckle.MultiTenantを選ぶのか

ASP.NET Core には、マルチテナント認証用の組み込みソリューションは用意されていません。Microsoft の公式ドキュメントでは、独自実装よりも以下の3つのフレームワークの利用を推奨しています。

  1. Orchard Core - モジュラー型のマルチテナントアプリケーションフレームワーク
  2. ABP Framework - DDD、マイクロサービス、マルチテナンシーをサポートする総合フレームワーク
  3. Finbuckle.MultiTenant - 軽量で柔軟なマルチテナント専用ライブラリ

実際に調査してみた結果、今回はFinbuckle.MultiTenantを選択しました。その理由は以下の通りです。

Finbuckle.MultiTenantの優位性

  1. 軽量性と専門性 - マルチテナント機能に特化しており、既存プロジェクトへの影響が最小限
  2. 柔軟なテナント識別戦略 - サブドメイン、ルート、ヘッダーなど複数の識別方法をサポート
  3. Entity Framework Coreとの深い統合 - グローバルクエリフィルターによる透過的なデータ分離
  4. ASP.NET Core Identityとの親和性 - 専用のMultiTenantIdentityDbContextによるスムーズな統合
  5. 学習コストの低さ - 標準的な.NET規約に従った直感的な設定方法

Orchard CoreやABP Frameworkは多機能で強力ですが、シンプルなマルチテナント実装には過剰な場合があります。Finbuckle.MultiTenantは必要十分な機能を提供しつつ、実装の複雑さを最小限に抑えられる点で優れています。(たぶん)

実装手順

1. ASP.NET Core認証認可アプリケーションの作成

新規プロジェクトの作成

Visual Studioで「ASP.NET Core Web アプリ (Model-View-Controller)」テンプレートを選択し、認証の種類を「個別のアカウント」に設定します。この設定により、ASP.NET Core Identityの基本的な実装が自動的に構成されます。

プロジェクト作成画面

Migrationの自動適用設定

開発環境では、アプリケーション起動時にデータベースマイグレーションを自動適用すると便利です。本番環境では、より厳密な管理が必要となります。

// Program.cs
var app = builder.Build();

// EF Core マイグレーションの自動適用
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var context = services.GetRequiredService<ApplicationDbContext>();
        context.Database.Migrate();
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "データベースマイグレーションの適用中にエラーが発生しました。");
        throw;
    }
}

この処理により、アプリケーション起動時に保留中のマイグレーションが自動的に適用されます。

必要なNuGetパッケージの追加

以下のツールを追加します。

  • Microsoft.EntityFrameworkCore.Design - dotnet efコマンド

初期マイグレーションの作成

パッケージマネージャーコンソールで以下を実行

dotnet ef migrations add InitialCreate -c ApplicationDbContext -p AuthTest.Web -o Data/Migrations

このコマンドにより、ASP.NET Core Identityの標準的なテーブル構造(AspNetUsers、AspNetRoles、AspNetUserClaims等)が作成されます。

2. Finbuckle.MultiTenantの統合

パッケージのインストール

以下のNuGetパッケージをインストールします。

  • Finbuckle.MultiTenant.AspNetCore - コアのマルチテナント機能
  • Finbuckle.MultiTenant.EntityFrameworkCore - Entity Framework Core統合

NuGetパッケージ画面

ApplicationDbContextの修正

マルチテナント対応のDbContextに変更します。これにより、すべてのIdentityテーブルにTenantId列が自動的に追加され、データのテナント分離が実現されます。

using Finbuckle.MultiTenant.Abstractions;
using Finbuckle.MultiTenant.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace AuthTest.Web.Data
{
    public class ApplicationDbContext : MultiTenantIdentityDbContext
    {
        public ApplicationDbContext(
            IMultiTenantContextAccessor multiTenantContextAccessor,
            DbContextOptions<ApplicationDbContext> options)
            : base(multiTenantContextAccessor, options)
        {
        }
    }
}

MultiTenantIdentityDbContextは、IdentityDbContextを継承し、テナント分離のための機能を追加したクラスです。IMultiTenantContextAccessorを通じて現在のテナント情報を取得し、自動的にクエリフィルターを適用します。

Program.csでのマルチテナント設定

// マルチテナント設定
builder.Services
    .AddMultiTenant<TenantInfo>()
    .WithInMemoryStore(options => options.Tenants = new List<TenantInfo>
    {
        new TenantInfo{Id="default", Identifier="default", Name="デフォルトテナント"},
        new TenantInfo{Id="tenant1", Identifier="tenant1", Name="テナント1"},
    })
    .WithHostStrategy() // サブドメインによるテナント識別
    .WithPerTenantAuthentication(); // テナントごとの認証分離

var app = builder.Build();

// マルチテナントミドルウェアの設定(UseAuthenticationより前に配置)
app.UseMultiTenant();

// ... 以下、標準的なミドルウェア設定

設定の重要ポイント

  1. WithHostStrategy() - サブドメインでテナントを識別します(例:tenant1.localhost)
  2. WithPerTenantAuthentication() - 各テナントで独立した認証セッションを維持します
  3. ミドルウェアの順序 - UseMultiTenant()はUseAuthentication()より前に配置する必要があります

3. データベースの再作成

マルチテナント機能を追加したため、データベース構造を更新する必要があります。

既存データベースの削除(開発環境のみ)

USE master;
IF DB_ID(N'YourDatabaseName') IS NOT NULL 
BEGIN 
    ALTER DATABASE [YourDatabaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [YourDatabaseName];
END

厳密には削除は不要ですが、最初にデータを作成していたため、削除しました。

新しいマイグレーションの作成

dotnet ef migrations add AddMultiTenantSupport -c ApplicationDbContext -p AuthTest.Web -o Data/Migrations

動作確認

アクセス方法と初期設定

テナントURLの形式

マルチテナント設定後は、以下の形式でアクセスします。

通常のlocalhostでアクセスすると、テナントが識別できないためエラーが発生します。

テナント未設定エラー画面

正常なアクセス画面

適切なテナントURLでアクセスすると、通常のASP.NET Core Identityの画面が表示されます。

正常なアクセス画面

データベース構造の確認

作成されたデータベースを確認すると、すべてのIdentityテーブルにTenantId列が追加されています。

データベース構造

この列により、各ユーザー、ロール、クレームなどが特定のテナントに紐付けられ、完全なデータ分離が実現されます。

テナント分離の動作確認

以下の手順でテナント分離が正しく機能していることを確認できます。

  1. defaultテナント(https://default.localhost:7120)でユーザーを作成
  2. 同じ認証情報でtenant1テナント(https://tenant1.localhost:7120)へのログインを試行
  3. ログインが失敗することを確認
  4. 逆のパターンでも同様に分離されていることを確認

トラブルシューティング

ユーザー登録はできるがログインできない場合

Program.cs内のミドルウェアが正しく登録されているか確認してください。

app.UseAuthentication();
app.UseAuthorization();

UseAuthentication()がUseAuthorization()の前に配置されていることを確認してください。

SSLエラーが発生する場合

開発環境では、サブドメインに対するSSL証明書の問題が発生することがあります。ブラウザの警告を一時的に無視して継続してください。

実装上の注意点

FindByIdAsyncの制限

Finbuckle.MultiTenantはグローバルクエリフィルターを使用してテナント分離を実現していますが、Entity FrameworkのFind()メソッドはこのフィルターをバイパスします。そのため、UserManager<TUser>.FindByIdAsyncメソッドは全テナントを検索対象とします。

テナントストアの選択肢

本記事ではインメモリストアを使用しましたが、プロダクション環境ではHttpContext、ヘッダー、パスなどの選択肢があります。

まとめ

本記事では、ASP.NET Core IdentityとFinbuckle.MultiTenantを組み合わせたマルチテナント認証認可を実装しました。

実装を通じて、マルチテナントアーキテクチャ自体は複雑であるものの、Finbuckle.MultiTenantを用いることで、想定以上にスムーズな実装が可能となりました。特に、Entity Framework Coreのグローバルクエリフィルターを活用した透過的なデータ分離は、開発者の負担を大幅に軽減します。たぶん。

また、FindByIdAsyncの制限など、いくつかの注意点はありますが、これらは実運用上は問題にはならないはずです。マルチテナントSaaSの開発において、Finbuckle.MultiTenantはとても便利であると感じました。

参考文献

ASP.NET Core での認証の概要 | Microsoft Learn
https://learn.microsoft.com/ja-jp/aspnet/core/security/authentication/?view=aspnetcore-9.0
ASP.NET Coreの認証に関する公式ドキュメント。マルチテナント認証フレームワークの推奨事項が記載されています。

Finbuckle.MultiTenant公式ドキュメント
https://www.finbuckle.com/multitenant
Finbuckle.MultiTenantの公式ドキュメント。各種ストア、戦略、設定方法について網羅的に解説されています。

Multi-tenancy - EF Core | Microsoft Learn
https://learn.microsoft.com/en-us/ef/core/miscellaneous/multitenancy
Entity Framework Coreでのマルチテナント実装パターンについて、Microsoftによる公式ガイドです。

ASP.NET Core Multi-tenancy: Data Isolation with Entity Framework - Ben Foster
https://benfoster.io/blog/aspnet-core-multi-tenancy-data-isolation-with-entity-framework/
Entity Frameworkを使用したデータ分離の実践的な実装例と考慮事項が詳しく解説されています。

Multi-tenant Apps With EF Core and ASP.​NET Core | The .NET Tools Blog
https://blog.jetbrains.com/dotnet/2022/06/22/multi-tenant-apps-with-ef-core-and-asp-net-core/
JetBrainsによるマルチテナントアプリケーション開発のベストプラクティス集です。

GitHub - Finbuckle/Finbuckle.MultiTenant
https://github.com/Finbuckle/Finbuckle.MultiTenant
Finbuckle.MultiTenantのソースコードとサンプルプロジェクト。実装の詳細を理解したい場合に最適です。

本記事のサンプルコード - GitHub
https://github.com/hydrangeas/AuthTest
本記事で解説した実装のサンプルコード。ASP.NET Core IdentityとFinbuckle.MultiTenantを統合した実際の動作例です。

GitHubで編集を提案

Discussion