ASP.NET Core 2.0 への認証と Identity の移行

作成者: Scott AddieHao Kung

ASP.NET Core 2.0 には、サービスを使用して簡単に構成できる、認証と Identity 用の新しいモデルが用意されています。 認証または Identity を使用する ASP.NET Core 1.x アプリケーションを、新しいモデルを使用するように更新することができます。以下では、これについて説明します。

名前空間を更新する

1.x では、IdentityRoleIdentityUser などのクラスは、Microsoft.AspNetCore.Identity.EntityFrameworkCore 名前空間に格納されていました。

2.0 では、Microsoft.AspNetCore.Identity 名前空間が、このようなクラスの一部の新しいホームになりました。 既定の Identity コードを使用する場合、影響を受けるクラスとしては、ApplicationUserStartup があります。 using ステートメントを調整して、影響を受ける参照を解決します。

認証ミドルウェアとサービス

1.x プロジェクトでは、ミドルウェアを介して認証を構成します。 サポートする認証スキームごとにミドルウェア メソッドを呼び出します。

次の 1.x の例では、Startup.cs で Identity を使用して Facebook 認証を構成しています。

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
    app.UseIdentity();
    app.UseFacebookAuthentication(new FacebookOptions {
        AppId = Configuration["auth:facebook:appid"],
        AppSecret = Configuration["auth:facebook:appsecret"]
    });
}

2.0 プロジェクトでは、サービスを介して認証を構成します。 各認証スキームは、Startup.csConfigureServices メソッドに登録されます。 UseIdentity メソッドは、UseAuthentication に置き換えられます。

次の 2.0 の例では、Startup.cs で Identity を使用して Facebook 認証を構成しています。

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

    // If you want to tweak Identity cookies, they're no longer part of IdentityOptions.
    services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
    services.AddAuthentication()
            .AddFacebook(options =>
            {
                options.AppId = Configuration["auth:facebook:appid"];
                options.AppSecret = Configuration["auth:facebook:appsecret"];
            });
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) {
    app.UseAuthentication();
}

UseAuthentication メソッドでは、1 つの認証ミドルウェア コンポーネントが追加され、これが、自動認証とリモート認証要求の処理を担当します。 これにより、個々のミドルウェアはすべて、単一の共通ミドルウェア コンポーネントに置き換えられます。

以下では、2.0 への移行手順を主要な認証スキーム別に説明します。

次の 2 つのオプションのいずれかを選択し、Startup.cs で必要な変更を行います。

  1. cookie と Identity を使用する

    • Configure メソッドで、UseIdentityUseAuthentication に置き換えます。

      app.UseAuthentication();
      
    • ConfigureServices メソッドで、AddIdentity メソッドを呼び出して cookie 認証サービスを追加します。

    • 必要に応じて、ConfigureServices メソッドで、ConfigureApplicationCookie または ConfigureExternalCookie メソッドを呼び出して、Identitycookie 設定を調整します。

      services.AddIdentity<ApplicationUser, IdentityRole>()
              .AddEntityFrameworkStores<ApplicationDbContext>()
              .AddDefaultTokenProviders();
      
      services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
      
  2. Identity を使用せずに cookie を使用する

    • Configure メソッドで、UseCookieAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

      app.UseAuthentication();
      
    • ConfigureServices メソッドで、AddAuthenticationAddCookie を呼び出します。

      // If you don't want the cookie to be automatically authenticated and assigned to HttpContext.User,
      // remove the CookieAuthenticationDefaults.AuthenticationScheme parameter passed to AddAuthentication.
      services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
              .AddCookie(options =>
              {
                  options.LoginPath = "/Account/LogIn";
                  options.LogoutPath = "/Account/LogOff";
              });
      

JWT ベアラー認証

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseJwtBearerAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddJwtBearer メソッドを呼び出します。

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Audience = "http://localhost:5001/";
                options.Authority = "http://localhost:5000/";
            });
    

    このコード スニペットでは Identity を使用しないため、JwtBearerDefaults.AuthenticationSchemeAddAuthentication メソッドに渡して既定のスキームを設定する必要があります。

OpenID Connect (OIDC) 認証

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseOpenIdConnectAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddOpenIdConnect メソッドを呼び出します。

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.Authority = Configuration["auth:oidc:authority"];
        options.ClientId = Configuration["auth:oidc:clientid"];
    });
    
  • OpenIdConnectOptions アクションの PostLogoutRedirectUri プロパティを SignedOutRedirectUri に置き換えます。

    .AddOpenIdConnect(options =>
    {
        options.SignedOutRedirectUri = "https://contoso.com";
    });
    

Facebook での認証

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseFacebookAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddFacebook メソッドを呼び出します。

    services.AddAuthentication()
            .AddFacebook(options =>
            {
                options.AppId = Configuration["auth:facebook:appid"];
                options.AppSecret = Configuration["auth:facebook:appsecret"];
            });
    

Google での認証

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseGoogleAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddGoogle メソッドを呼び出します。

    services.AddAuthentication()
            .AddGoogle(options =>
            {
                options.ClientId = Configuration["auth:google:clientid"];
                options.ClientSecret = Configuration["auth:google:clientsecret"];
            });
    

Microsoft アカウント認証

Microsoft アカウント認証の詳細については、この GitHub イシューを参照してください。

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseMicrosoftAccountAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddMicrosoftAccount メソッドを呼び出します。

    services.AddAuthentication()
            .AddMicrosoftAccount(options =>
            {
                options.ClientId = Configuration["auth:microsoft:clientid"];
                options.ClientSecret = Configuration["auth:microsoft:clientsecret"];
            });
    

Twitter での認証

Startup.cs で次の変更を行います。

  • Configure メソッドで、UseTwitterAuthentication メソッド呼び出しを UseAuthentication に置き換えます。

    app.UseAuthentication();
    
  • ConfigureServices メソッドで、AddTwitter メソッドを呼び出します。

    services.AddAuthentication()
            .AddTwitter(options =>
            {
                options.ConsumerKey = Configuration["auth:twitter:consumerkey"];
                options.ConsumerSecret = Configuration["auth:twitter:consumersecret"];
            });
    

既定の認証スキームの設定

1.x では、AuthenticationOptions 基底クラスの AutomaticAuthenticate および AutomaticChallenge プロパティは、単一の認証スキームについて設定するためのものでした。 これを適用する適切な方法がありませんでした。

2.0 では、個々の AuthenticationOptions インスタンスのプロパティとして使用されていたこれら 2 つのプロパティは削除されました。 これらは、Startup.csConfigureServices メソッド内の AddAuthentication メソッド呼び出しで構成できます。

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

前述のコード スニペットでは、既定のスキームを CookieAuthenticationDefaults.AuthenticationScheme ("Cookies") に設定しています。

または、AddAuthentication メソッドのオーバーロードされたバージョンを使用して、複数のプロパティを設定します。 次のオーバーロードされたメソッドの例では、既定のスキームを CookieAuthenticationDefaults.AuthenticationScheme に設定しています。 または、個々の [Authorize] 属性または承認ポリシー内で認証スキームを指定することもできます。

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});

次のいずれかの条件に該当する場合、2.0 の既定のスキームを定義します。

  • ユーザーを自動的にサインインさせる
  • スキームを指定せずに [Authorize] 属性または承認ポリシーを使用する

この規則の例外は、AddIdentity メソッドです。 このメソッドでは、cookie を追加し、既定の認証およびチャレンジ スキームをアプリケーション cookie の IdentityConstants.ApplicationScheme に設定します。 または、既定のサインイン スキームが外部 cookie の IdentityConstants.ExternalScheme に設定されます。

HttpContext 認証拡張機能を使用する

IAuthenticationManager インターフェイスは、1.x 認証システムへの主要なエントリ ポイントです。 これは、Microsoft.AspNetCore.Authentication 名前空間内の新しい HttpContext 拡張メソッド セットに置き換えられました。

たとえば、1.x プロジェクトでは、Authentication プロパティを参照します。

// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);

2.0 プロジェクトでは、Microsoft.AspNetCore.Authentication 名前空間をインポートし、Authentication プロパティ参照を削除します。

// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

Windows 認証 (HTTP.sys / IISIntegration)

Windows 認証には、次の 2 つのバリエーションがあります。

  • ホストでは、認証されたユーザーのみが許可されます。 このバリエーションは、2.0 の変更の影響を受けません。

  • ホストでは、匿名ユーザーと認証されたユーザーの両方が許可されます。 このバリエーションは、2.0 の変更の影響を受けます。 たとえば、アプリでは、匿名ユーザーを IIS または HTTP.sys レイヤーで許可し、コントローラー レベルで承認する必要があります。 このシナリオでは、既定のスキームを Startup.ConfigureServices メソッドで設定します。

    Microsoft.AspNetCore.Server.IISIntegrationの場合は、既定のスキームを IISDefaults.AuthenticationScheme に設定します。

    using Microsoft.AspNetCore.Server.IISIntegration;
    
    services.AddAuthentication(IISDefaults.AuthenticationScheme);
    

    Microsoft.AspNetCore.Server.HttpSysの場合は、既定のスキームを HttpSysDefaults.AuthenticationScheme に設定します。

    using Microsoft.AspNetCore.Server.HttpSys;
    
    services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
    

    既定のスキームを設定しないと、承認 (チャレンジ) 要求が機能しなくなり、次の例外が発生します。

    System.InvalidOperationException: authenticationScheme が指定されておらず、DefaultChallengeScheme が見つかりませんでした。

詳細については、「ASP.NET Core で Windows 認証を構成する」を参照してください。

IdentityCookieOptions インスタンス

2.0 の変更による副作用は、cookie オプション インスタンスの代わりに名前付きオプションを使用するように切り替えられたことです。 Identitycookie スキーム名をカスタマイズする機能は削除されます。

たとえば、1.x プロジェクトでは、コンストラクターの挿入を使用して、IdentityCookieOptions パラメーターを AccountController.csManageController.cs に渡します。 外部 cookie 認証スキームは、指定されたインスタンスからアクセスされます。

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IOptions<IdentityCookieOptions> identityCookieOptions,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
}

2.0 プロジェクトでは、前述のコンストラクターの挿入は不要になり、_externalCookieScheme フィールドを削除することができます。

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
}

1.x プロジェクトでは、_externalCookieScheme フィールドは次のように使用されていました。

// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);

2.0 プロジェクトでは、上記のコードを次のコードに置き換えます。 IdentityConstants.ExternalScheme 定数を直接使用できます。

// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

次の名前空間をインポートして、新しく追加された SignOutAsync 呼び出しを解決します。

using Microsoft.AspNetCore.Authentication;

IdentityUser POCO ナビゲーション プロパティを追加する

基本 IdentityUser POCO (単純な従来の CLR オブジェクト) の Entity Framework (EF) Core ナビゲーション プロパティは削除されました。 1.x プロジェクトでこれらのプロパティを使用していた場合は、あらためてこれらを手動で 2.0 プロジェクトに追加します。

/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>();

/// <summary>
/// Navigation property for the claims this user possesses.
/// </summary>
public virtual ICollection<IdentityUserClaim<int>> Claims { get; } = new List<IdentityUserClaim<int>>();

/// <summary>
/// Navigation property for this users login accounts.
/// </summary>
public virtual ICollection<IdentityUserLogin<int>> Logins { get; } = new List<IdentityUserLogin<int>>();

EF Core 移行の実行時に外部キーが重複しないようにするには、次のコードを IdentityDbContext クラスの OnModelCreating メソッド (base.OnModelCreating(); 呼び出しの後) に追加します。

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    // Customize the ASP.NET Core Identity model and override the defaults if needed.
    // For example, you can rename the ASP.NET Core Identity table names and more.
    // Add your customizations after calling base.OnModelCreating(builder);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Claims)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Logins)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Roles)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);
}

GetExternalAuthenticationSchemes を置き換える

非同期バージョンを優先するために、同期メソッド GetExternalAuthenticationSchemes は削除されました。 1.x プロジェクトでは、Controllers/ManageController.cs に次のコードがあります。

var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();

このメソッドは、Views/Account/Login.cshtml にも表示されます。

@{
    var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
    if (loginProviders.Count == 0)
    {
        <div>
            <p>
                There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                for details on setting up this ASP.NET application to support logging in via external services.
            </p>
        </div>
    }
    else
    {
        <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
            <div>
                <p>
                    @foreach (var provider in loginProviders)
                    {
                        <button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
                    }
                </p>
            </div>
        </form>
    }
}

2.0 プロジェクトでは、GetExternalAuthenticationSchemesAsync メソッドを使用します。 ManageController.cs の変更は、次のコードのようになります。

var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync();
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();

Login.cshtml では、foreach ループでアクセスされる AuthenticationScheme プロパティが Name に変更されます。

@{
    var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    if (loginProviders.Count == 0)
    {
        <div>
            <p>
                There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                for details on setting up this ASP.NET application to support logging in via external services.
            </p>
        </div>
    }
    else
    {
        <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
            <div>
                <p>
                    @foreach (var provider in loginProviders)
                    {
                        <button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                    }
                </p>
            </div>
        </form>
    }
}

ManageLoginsViewModel プロパティの変更

ManageLoginsViewModel オブジェクトは、ManageController.csManageLogins アクションで使用されます。 1.x プロジェクトでは、このオブジェクトの OtherLogins プロパティの戻り値の型は、IList<AuthenticationDescription> です。 この戻り値の型では、Microsoft.AspNetCore.Http.Authentication のインポートが必要です。

using System.Collections.Generic;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;

namespace AspNetCoreDotNetCore1App.Models.ManageViewModels
{
    public class ManageLoginsViewModel
    {
        public IList<UserLoginInfo> CurrentLogins { get; set; }

        public IList<AuthenticationDescription> OtherLogins { get; set; }
    }
}

2.0 プロジェクトでは、戻り値の型が IList<AuthenticationScheme> に変更されます。 この新しい戻り値の型では、Microsoft.AspNetCore.Http.Authentication のインポートを Microsoft.AspNetCore.Authentication のインポートに置き換える必要があります。

using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;

namespace AspNetCoreDotNetCore2App.Models.ManageViewModels
{
    public class ManageLoginsViewModel
    {
        public IList<UserLoginInfo> CurrentLogins { get; set; }

        public IList<AuthenticationScheme> OtherLogins { get; set; }
    }
}

その他のリソース

詳細については、GitHub イシュー「Auth 2.0 の説明」を参照してください。