ASP.NET Core でのアカウントの確認とパスワードの回復
作成者: Rick Anderson、Ponant、Joe 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" は最初にローカル ログインとして作成されています。ただし、アカウントを最初にソーシャル ログインとして作成してから、ローカル ログインを追加することもできます。
[管理] リンクをクリックします。 このアカウントに関連付けられている外部 (ソーシャル ログイン) が 0 であることに注意してください。
別のログイン サービスへのリンクをクリックし、アプリの要求を受け入れます。 次の図では、Facebook が外部認証プロバイダーです。
2 つのアカウントが結合されています。 いずれかのアカウントでサインインできます。 ソーシャル ログイン認証サービスがダウンしたときのため、またはさらに可能性が高いのはソーシャル アカウントにアクセスできなくなったときのために、ユーザーにローカル アカウントを追加させたいことがあります。
サイトにユーザーがいる用になった後でアカウントの確認を有効にする
ユーザーがいるサイトでアカウントの確認を有効にすると、すべての既存のユーザーがロックアウトされます。 既存のユーザーは、アカウントが確認されていないためにロックアウトされます。 既存のユーザーのロックアウトを回避するには、次のいずれかの方法を使用します。
- データベースを更新して、既存のすべてのユーザーを確認済みとしてマークします。
- 既存のユーザーを確認します。 たとえば、確認リンクを含むメールを一括送信します。
前提条件
認証を備えた 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" は最初にローカル ログインとして作成されています。ただし、アカウントを最初にソーシャル ログインとして作成してから、ローカル ログインを追加することもできます。
[管理] リンクをクリックします。 このアカウントに関連付けられている外部 (ソーシャル ログイン) が 0 であることに注意してください。
別のログイン サービスへのリンクをクリックし、アプリの要求を受け入れます。 次の図では、Facebook が外部認証プロバイダーです。
2 つのアカウントが結合されています。 いずれかのアカウントでサインインできます。 ソーシャル ログイン認証サービスがダウンしたときのため、またはさらに可能性が高いのはソーシャル アカウントにアクセスできなくなったときのために、ユーザーにローカル アカウントを追加させたいことがあります。
サイトにユーザーがいる用になった後でアカウントの確認を有効にする
ユーザーがいるサイトでアカウントの確認を有効にすると、すべての既存のユーザーがロックアウトされます。 既存のユーザーは、アカウントが確認されていないためにロックアウトされます。 既存のユーザーのロックアウトを回避するには、次のいずれかの方法を使用します。
- データベースを更新して、既存のすべてのユーザーを確認済みとしてマークします。
- 既存のユーザーを確認します。 たとえば、確認リンクを含むメールを一括送信します。
ASP.NET Core