ASP.NET Identity でカスタムクレームを使う

マルチテナントな Web サービスを作っていて、 どのテナントかの判断を現在ログインしているユーザーが持っているテナント ID で判断している。

そのため、HttpContext.User.Identity.GetUserId() で取得したユーザー ID を使って、 データベースからユーザーを取得してテナント ID を取り出していた。

でも最近、テナント ID をカスタムクレームに追加すればいいことに気付いた。

public static class CustomClaimTypes
{
    public const string TenantId = "http://example.org/claims/tenantid";
}

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

        // カスタムクレームを追加する
        userIdentity.AddClaim(new Claim(CustomClaimTypes.TenantId, this.TenantId.ToString()));

        return userIdentity;
    }
    
    public int TenantId { get; set; }
}

public static class IdentityExtensions
{
    public static int? GetTenantId(this IIdentity identity)
    {
        var claimsIdentity = identity as ClaimsIdentity;
        if (claimsIdentity != null)
        {
            var claim = claimsIdentity.Claims.FirstOrDefault(c => c.Type == CustomClaimTypes.TenantId);
            if (claim != null)
            {
                int tenantId;
                if (int.TryParse(claim.Value, out tenantId))
                {
                    return tenantId;
                }
            }
        }
        return null;
    }
}

コントローラーからは HttpContext からたどって取得できる。

this.HttpContext.User.Identity.GetTenantId();

テナントごとに違うサブドメインにできたら、サブドメインでテナントを判別できて一番いいんだけどね。 大人の事情でそれはやらないことになったから、仕方ない。