ASP.NET Core でのアカウントの確認とパスワードの回復

作成者: Rick AndersonPonantJoe Audette

このチュートリアルでは、メールの確認とパスワードのリセットを備えた ASP.NET Core アプリを構築する方法について説明します。 このチュートリアルは最初のトピックではありません。 次のことを理解している必要があります。

Blazor のガイダンスについては、「ASP.NET Core Blazor におけるアカウントの確認とパスワード」を参照してください。

前提条件

認証を備えた Web アプリを作成してテストする

認証機能を備えた Web アプリを作成するには、次のコマンドを実行します。

dotnet new webapp -au Individual -o WebPWrecover
cd WebPWrecover
dotnet run

シミュレート済みのメール確認にユーザーを登録する

アプリを実行し、[登録] リンクを選んで、ユーザーを登録します。 登録が済むと、メールの確認をシミュレートするためのリンクが含まれる /Identity/Account/RegisterConfirmation ページにリダイレクトされます。

  • [Click here to confirm your account](ログの一元化) リンクを選択します。
  • [ログイン] リンクを選び、同じ資格情報でサインインします。
  • Hello YourEmail@provider.com! リンクを選ぶと、/Identity/Account/Manage/PersonalData ページにリダイレクトされます。
  • 左側の [個人データ] タブを選び、[削除] を選びます。

Click here to confirm your account リンクが表示されるのは、IEmailSender が実装されておらず、依存関係挿入コンテナーに登録されていないためです。 RegisterConfirmationソースを参照してください。

Note

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

メール プロバイダーを構成する

このチュートリアルでは、SendGrid を使ってメールを送信します。 メールを送信するには、SendGrid のアカウントとキーが必要です。 メール送信には、SMTP ではなく、SendGrid や他のメール サービスを使うことをお勧めします。 SMTP を正しくセキュリティ保護して設定するのは簡単ではありません。

SendGrid アカウントでは、送信者の追加が必要になる場合があります。

セキュリティ保護されたメール キーをフェッチするためのクラスを作成します。 このサンプルでは、Services/AuthMessageSenderOptions.cs を作成します。

namespace WebPWrecover.Services;

public class AuthMessageSenderOptions
{
    public string? SendGridKey { get; set; }
}

SendGrid のユーザー シークレットを構成する

シークレット マネージャー ツールを使用して、SendGridKey を設定します。 次に例を示します。

dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridKey to the secret store.

Windows では、シークレット マネージャーによって、%APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> ディレクトリの secrets.json ファイルに、キーと値のペアが格納されます。

secrets.json ファイルの内容は暗号化されていません。 次のマークアップは secrets.json ファイルを示しています。 SendGridKey の値は削除されています。

{
  "SendGridKey": "<key removed>"
}

詳しくは、オプション パターン構成に関するページをご覧ください。

SendGrid をインストールする

このチュートリアルでは、SendGrid を使ってメール通知を追加する方法について説明しますが、他のメール プロバイダーを使うこともできます。

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

パッケージ マネージャー コンソールから、次のコマンドを入力します。

Install-Package SendGrid

無料の SendGrid アカウントに登録するには、「SendGrid を無料で使い始める」をご覧ください。

IEmailSender を実装する

IEmailSender を実装するには、次のようなコードを使用して、Services/EmailSender.cs を作成します。

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.SendGridKey))
        {
            throw new Exception("Null SendGridKey");
        }
        await Execute(Options.SendGridKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

メールをサポートするようにアプリを構成する

次のコードを Program.cs ファイルに追加します。

  • 一時的なサービスとして EmailSender を追加します。
  • AuthMessageSenderOptions 構成インスタンスを登録します。
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

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

app.MapRazorPages();

app.Run();

Account.RegisterConfirmation がスキャフォールディングされたときに既定のアカウント検証を無効にする

このセクションは、Account.RegisterConfirmation がスキャフォールディングされる場合にのみ適用されます。 Account.RegisterConfirmation をスキャフォールディングしていない場合は、このセクションをスキップしてください。

ユーザーは Account.RegisterConfirmation にリダイレクトされ、そこでアカウントを確認するリンクを選択できます。 既定値 Account.RegisterConfirmation は、テスト目的で "のみ" 使用されます。運用アプリでは、自動アカウント検証を無効にしてください。

確認済みのアカウントを必須にして、登録時に即時ログインできないようにするには、スキャフォールディングされた /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs ファイルで DisplayConfirmAccountLink = false を設定します。

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

この手順は、Account.RegisterConfirmation がスキャフォールディングされる場合にのみ必要です。 スキャフォールディングされていない RegisterConfirmation の場合、IEmailSender が実装され、依存関係挿入コンテナー コンテナーに登録されると、これを自動的に検出します。

登録し、メールを確認し、パスワードをリセットする

Web アプリを実行し、アカウント確認とパスワード回復のフローをテストします。

  • アプリを実行して新しいユーザーを登録します
  • メールでアカウント確認リンクを調べます。 メールを受信しない場合は、「メールをデバッグする」をご覧ください。
  • リンクをクリックして、メールを確認します。
  • メールとパスワードを使ってサインインします。
  • サインアウトします。

パスワードのリセットをテストする

  • サインインしている場合は、[ログアウト] を選びます。
  • [ログイン] リンクを選び、[パスワードを忘れた場合] リンクを選びます。
  • アカウントの登録に使ったメール アドレスを入力します。
  • パスワードをリセットするためのリンクを含むメールが送信されます。 メールを確認し、リンクをクリックしてパスワードをリセットします。 パスワードが正常にリセットされたら、メール アドレスと新しいパスワードでサインインできます。

メールの再送信の確認

[ログイン] ページで [Resend email confirmation]\(メールの再送信の確認\) リンクを選びます。

メールとアクティビティのタイムアウトを変更する

非アクティブ状態の既定のタイムアウトは 14 日です。 次のコードでは、非アクティブ状態のタイムアウトが 5 日に設定されます。

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

builder.Services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

var app = builder.Build();

// Code removed for brevity

すべてのデータ保護トークンの有効期限を変更する

次のコードでは、すべてのデータ保護トークンのタイムアウト期間が 3 時間に変更されます。

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

builder.Services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

var app = builder.Build();

// Code removed for brevity.

組み込みの Identity ユーザー トークン (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs を参照) のタイムアウトは 1 日です。

メール トークンの有効期間を変更する

Identity ユーザー トークンの既定のトークン有効期間は 1 日です。 このセクションでは、メール トークンの有効期間を変更する方法について説明します。

カスタムの DataProtectorTokenProvider<TUser>DataProtectionTokenProviderOptions を追加します。

public class CustomEmailConfirmationTokenProvider<TUser>
                              :  DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
                                       : base(dataProtectionProvider, options, logger)
    {

    }
}
public class EmailConfirmationTokenProviderOptions : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

カスタム プロバイダーをサービス コンテナーに追加します。

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;
using WebPWrecover.TokenProviders;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(config =>
{
    config.SignIn.RequireConfirmedEmail = true;
    config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
        new TokenProviderDescriptor(
            typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
    config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
}).AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

// Code removed for brevity.

メールをデバッグする

メールが機能しない場合:

  • EmailSender.Execute にブレークポイントを設定して、SendGridClient.SendEmailAsync が呼び出されることを確認します。
  • EmailSender.Execute と同様のコードを使って、メールを送信するコンソール アプリを作成します。
  • メール アクティビティ ページを確認します。
  • 迷惑メール フォルダーを確認します。
  • 別のメール プロバイダー (Microsoft、Yahoo、Gmail など) で別のメール エイリアスを試します。
  • 別のメール アカウントに送信してみます。

セキュリティのベスト プラクティスは、テストと開発では運用用のシークレットを使用しないことです。 アプリを Azure に発行する場合は、Azure Web アプリ ポータルで SendGrid シークレットをアプリケーション設定として設定します。 構成システムは、環境変数からキーを読み取るように設定されています。

ソーシャルとローカルのログイン アカウントを結合する

このセクションを完了するには、最初に外部認証プロバイダーを有効にする必要があります。 Facebook、Google、外部プロバイダーの認証に関するページをご覧ください。

メールのリンクをクリックして、ローカル アカウントとソーシャル アカウントを組み合わせることができます。 次のシーケンスでは、"RickAndMSFT@gmail.com" は最初にローカル ログインとして作成されています。ただし、アカウントを最初にソーシャル ログインとして作成してから、ローカル ログインを追加することもできます。

Web application: RickAndMSFT@gmail.com user authenticated

[管理] リンクをクリックします。 このアカウントに関連付けられている外部 (ソーシャル ログイン) が 0 であることに注意してください。

Manage view

別のログイン サービスへのリンクをクリックし、アプリの要求を受け入れます。 次の図では、Facebook が外部認証プロバイダーです。

Manage your external logins view listing Facebook

2 つのアカウントが結合されています。 いずれかのアカウントでサインインできます。 ソーシャル ログイン認証サービスがダウンしたときのため、またはさらに可能性が高いのはソーシャル アカウントにアクセスできなくなったときのために、ユーザーにローカル アカウントを追加させたいことがあります。

サイトにユーザーがいる用になった後でアカウントの確認を有効にする

ユーザーがいるサイトでアカウントの確認を有効にすると、すべての既存のユーザーがロックアウトされます。 既存のユーザーは、アカウントが確認されていないためにロックアウトされます。 既存のユーザーのロックアウトを回避するには、次のいずれかの方法を使用します。

  • データベースを更新して、既存のすべてのユーザーを確認済みとしてマークします。
  • 既存のユーザーを確認します。 たとえば、確認リンクを含むメールを一括送信します。

前提条件

.NET Core 3.0 SDK 以降

認証を備えた Web アプリを作成してテストする

認証機能を備えた Web アプリを作成するには、次のコマンドを実行します。

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet run

アプリを実行し、[登録] リンクを選んで、ユーザーを登録します。 登録が済むと、メールの確認をシミュレートするためのリンクが含まれる /Identity/Account/RegisterConfirmation ページにリダイレクトされます。

  • [Click here to confirm your account](ログの一元化) リンクを選択します。
  • [ログイン] リンクを選び、同じ資格情報でサインインします。
  • Hello YourEmail@provider.com! リンクを選ぶと、/Identity/Account/Manage/PersonalData ページにリダイレクトされます。
  • 左側の [個人データ] タブを選び、[削除] を選びます。

メール プロバイダーを構成する

このチュートリアルでは、SendGrid を使ってメールを送信します。 他のメール プロバイダーを使ってもかまいません。 SendGrid または別のメール サービスを使ってメールを送信することをお勧めします。 SMTP では、メールがスパムとマークされないように構成することは困難です。

SendGrid アカウントでは、送信者の追加が必要になる場合があります。

セキュリティ保護されたメール キーをフェッチするためのクラスを作成します。 このサンプルでは、Services/AuthMessageSenderOptions.cs を作成します。

namespace WebPWrecover.Services;

public class AuthMessageSenderOptions
{
    public string? SendGridKey { get; set; }
}

SendGrid のユーザー シークレットを構成する

シークレット マネージャー ツールを使用して、SendGridKey を設定します。 次に例を示します。

dotnet user-secrets set SendGridKey <SG.key>

Successfully saved SendGridKey = SG.keyVal to the secret store.

Windows では、シークレット マネージャーによって、%APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> ディレクトリの secrets.json ファイルに、キーと値のペアが格納されます。

secrets.json ファイルの内容は暗号化されていません。 次のマークアップは secrets.json ファイルを示しています。 SendGridKey の値は削除されています。

{
  "SendGridKey": "<key removed>"
}

詳しくは、オプション パターン構成に関するページをご覧ください。

SendGrid をインストールする

このチュートリアルでは、SendGrid によるメール通知を追加する方法を示しますが、SMTP や他のメカニズムを使ってメールを送信することもできます。

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

パッケージ マネージャー コンソールから、次のコマンドを入力します。

Install-Package SendGrid

無料の SendGrid アカウントに登録するには、「SendGrid を無料で使い始める」をご覧ください。

IEmailSender を実装する

IEmailSender を実装するには、次のようなコードを使用して、Services/EmailSender.cs を作成します。

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace WebPWrecover.Services;

public class EmailSender : IEmailSender
{
    private readonly ILogger _logger;

    public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
                       ILogger<EmailSender> logger)
    {
        Options = optionsAccessor.Value;
        _logger = logger;
    }

    public AuthMessageSenderOptions Options { get; } //Set with Secret Manager.

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.SendGridKey))
        {
            throw new Exception("Null SendGridKey");
        }
        await Execute(Options.SendGridKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, string toEmail)
    {
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new EmailAddress("Joe@contoso.com", "Password Recovery"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new EmailAddress(toEmail));

        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);
        var response = await client.SendEmailAsync(msg);
        _logger.LogInformation(response.IsSuccessStatusCode 
                               ? $"Email to {toEmail} queued successfully!"
                               : $"Failure Email to {toEmail}");
    }
}

メールをサポートするためのスタートアップを構成する

Startup.cs ファイルの ConfigureServices メソッドに次のコードを追加します。

  • 一時的なサービスとして EmailSender を追加します。
  • AuthMessageSenderOptions 構成インスタンスを登録します。
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using WebPWrecover.Data;
using WebPWrecover.Services;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

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

app.MapRazorPages();

app.Run();

RegisterConfirmation をスキャフォールディングします

Identity のスキャフォールディングに関するページの説明に従い、Account\RegisterConfirmation をスキャフォールディングします。

Account.RegisterConfirmation がスキャフォールディングされたときに既定のアカウント検証を無効にする

このセクションは、Account.RegisterConfirmation がスキャフォールディングされる場合にのみ適用されます。 Account.RegisterConfirmation をスキャフォールディングしていない場合は、このセクションをスキップしてください。

ユーザーは Account.RegisterConfirmation にリダイレクトされ、そこでアカウントを確認するリンクを選択できます。 既定値 Account.RegisterConfirmation は、テスト目的で "のみ" 使用されます。運用アプリでは、自動アカウント検証を無効にしてください。

確認済みのアカウントを必須にして、登録時に即時ログインできないようにするには、スキャフォールディングされた /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs ファイルで DisplayConfirmAccountLink = false を設定します。

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;

namespace WebPWrecover.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _sender;

        public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
        {
            _userManager = userManager;
            _sender = sender;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public bool DisplayConfirmAccountLink { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string EmailConfirmationUrl { get; set; }

        public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
        {
            if (email == null)
            {
                return RedirectToPage("/Index");
            }
            returnUrl = returnUrl ?? Url.Content("~/");

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return NotFound($"Unable to load user with email '{email}'.");
            }

            Email = email;
            // Once you add a real email sender, you should remove this code that lets you confirm the account
            DisplayConfirmAccountLink = false;
            if (DisplayConfirmAccountLink)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                EmailConfirmationUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
            }

            return Page();
        }
    }
}

この手順は、Account.RegisterConfirmation がスキャフォールディングされる場合にのみ必要です。 スキャフォールディングされていない RegisterConfirmation の場合、IEmailSender が実装され、依存関係挿入コンテナー コンテナーに登録されると、これを自動的に検出します。

登録し、メールを確認し、パスワードをリセットする

Web アプリを実行し、アカウント確認とパスワード回復のフローをテストします。

  • アプリを実行して新しいユーザーを登録します
  • メールでアカウント確認リンクを調べます。 メールを受信しない場合は、「メールをデバッグする」をご覧ください。
  • リンクをクリックして、メールを確認します。
  • メールとパスワードを使ってサインインします。
  • サインアウトします。

パスワードのリセットをテストする

  • サインインしている場合は、[ログアウト] を選びます。
  • [ログイン] リンクを選び、[パスワードを忘れた場合] リンクを選びます。
  • アカウントの登録に使ったメール アドレスを入力します。
  • パスワードをリセットするためのリンクを含むメールが送信されます。 メールを確認し、リンクをクリックしてパスワードをリセットします。 パスワードが正常にリセットされたら、メール アドレスと新しいパスワードでサインインできます。

メールの再送信の確認

ASP.NET Core 5.0 以降では、[ログイン]ページの [電子メールの再送信の確認] リンクを選びます。

メールとアクティビティのタイムアウトを変更する

非アクティブ状態の既定のタイムアウトは 14 日です。 次のコードでは、非アクティブ状態のタイムアウトが 5 日に設定されます。

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

すべてのデータ保護トークンの有効期限を変更する

次のコードでは、すべてのデータ保護トークンのタイムアウト期間が 3 時間に変更されます。

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

組み込みの Identity ユーザー トークン (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs を参照) のタイムアウトは 1 日です。

メール トークンの有効期間を変更する

Identity ユーザー トークンの既定のトークン有効期間は 1 日です。 このセクションでは、メール トークンの有効期間を変更する方法について説明します。

カスタムの DataProtectorTokenProvider<TUser>DataProtectionTokenProviderOptions を追加します。

public class CustomEmailConfirmationTokenProvider<TUser>
                                       : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
                                          : base(dataProtectionProvider, options, logger)
    {

    }
}
public class EmailConfirmationTokenProviderOptions : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

カスタム プロバイダーをサービス コンテナーに追加します。

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
      }).AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

メールをデバッグする

メールが機能しない場合:

  • EmailSender.Execute にブレークポイントを設定して、SendGridClient.SendEmailAsync が呼び出されることを確認します。
  • EmailSender.Execute と同様のコードを使って、メールを送信するコンソール アプリを作成します。
  • メール アクティビティ ページを確認します。
  • 迷惑メール フォルダーを確認します。
  • 別のメール プロバイダー (Microsoft、Yahoo、Gmail など) で別のメール エイリアスを試します。
  • 別のメール アカウントに送信してみます。

セキュリティのベスト プラクティスは、テストと開発では運用用のシークレットを使用しないことです。 アプリを Azure に発行する場合は、Azure Web アプリ ポータルで SendGrid シークレットをアプリケーション設定として設定します。 構成システムは、環境変数からキーを読み取るように設定されています。

ソーシャルとローカルのログイン アカウントを結合する

このセクションを完了するには、最初に外部認証プロバイダーを有効にする必要があります。 Facebook、Google、外部プロバイダーの認証に関するページをご覧ください。

メールのリンクをクリックして、ローカル アカウントとソーシャル アカウントを組み合わせることができます。 次のシーケンスでは、"RickAndMSFT@gmail.com" は最初にローカル ログインとして作成されています。ただし、アカウントを最初にソーシャル ログインとして作成してから、ローカル ログインを追加することもできます。

Web application: RickAndMSFT@gmail.com user authenticated

[管理] リンクをクリックします。 このアカウントに関連付けられている外部 (ソーシャル ログイン) が 0 であることに注意してください。

Manage view

別のログイン サービスへのリンクをクリックし、アプリの要求を受け入れます。 次の図では、Facebook が外部認証プロバイダーです。

Manage your external logins view listing Facebook

2 つのアカウントが結合されています。 いずれかのアカウントでサインインできます。 ソーシャル ログイン認証サービスがダウンしたときのため、またはさらに可能性が高いのはソーシャル アカウントにアクセスできなくなったときのために、ユーザーにローカル アカウントを追加させたいことがあります。

サイトにユーザーがいる用になった後でアカウントの確認を有効にする

ユーザーがいるサイトでアカウントの確認を有効にすると、すべての既存のユーザーがロックアウトされます。 既存のユーザーは、アカウントが確認されていないためにロックアウトされます。 既存のユーザーのロックアウトを回避するには、次のいずれかの方法を使用します。

  • データベースを更新して、既存のすべてのユーザーを確認済みとしてマークします。
  • 既存のユーザーを確認します。 たとえば、確認リンクを含むメールを一括送信します。