Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、電子メールの確認とパスワードの回復を使用して、ASP.NET Core Blazor WebAssembly を使用して ASP.NET Core Identity アプリを構成する方法について説明します。
Note
この記事では、ASP.NET Core Blazor WebAssemblyを使用したスタンドアロン Identity アプリのみを適用します。 Blazor Web Appの電子メールの確認とパスワードの回復を実装するには、ASP.NET Core Blazorのアカウントの確認とパスワードの回復に関するを参照してください。
名前空間と記事コードの例
この記事の例で使用される名前空間は次のとおりです。
Backend
バックエンド サーバー Web API プロジェクト (この記事の「サーバー プロジェクト」)。BlazorWasmAuth
フロントエンド クライアント スタンドアロン Blazor WebAssembly アプリの場合 (この記事の「クライアント プロジェクト」)。
これらの名前空間は、BlazorWebAssemblyStandaloneWithIdentity
GitHub リポジトリのdotnet/blazor-samples
サンプル ソリューション内のプロジェクト対応します。 詳細については、「ASP.NET Core Blazor WebAssemblyIdentityセキュア ASP.NET Core 」を参照してください。
BlazorWebAssemblyStandaloneWithIdentity
サンプル ソリューションを使用していない場合は、プロジェクトの名前空間を使用するようにコード例の名前空間を変更します。
サーバー プロジェクトの電子メール プロバイダーを選択して構成する
この記事では、Mailchimp のトランザクション API を使用して Mandrill.net 経由でメールを送信します。 メール送信には、SMTP ではなく、メール サービスを使うことをお勧めします。 SMTP を適切に構成してセキュリティで保護することは困難です。 どのメール サービスを使用する場合でも、.NET アプリのガイダンスにアクセスし、アカウントを作成し、サービスの API キーを構成し、必要な NuGet パッケージをインストールします。
サーバー プロジェクトで、シークレット電子メール プロバイダー API キーを保持するクラスを作成します。 この記事の例では、AuthMessageSenderOptions
プロパティを持つ EmailAuthKey
という名前のクラスを使用してキーを保持します。
AuthMessageSenderOptions.cs
:
namespace Backend;
public class AuthMessageSenderOptions
{
public string? EmailAuthKey { get; set; }
}
AuthMessageSenderOptions
構成インスタンスをサーバー プロジェクトのProgram
ファイルに登録します。
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
電子メール プロバイダーのセキュリティ キーのシークレットを構成する
プロバイダーから電子メール プロバイダーのセキュリティ キーを受け取り、次のガイダンスで使用します。
次のいずれかの方法または両方の方法を使用して、シークレットをアプリに提供します。
- Secret Manager ツール: シークレット マネージャー ツールは、ローカル コンピューターにプライベート データを格納し、ローカル開発時にのみ使用します。
- Azure Key Vault: ローカルで作業するときに開発環境を含め、任意の環境で使用するためにシークレットをキー コンテナーに格納できます。 一部の開発者は、ステージングおよび運用環境のデプロイにキー コンテナーを使用し、ローカル開発に Secret Manager ツールを使用することを好みます。
プロジェクト コードまたは構成ファイルにシークレットを格納しないことを強くお勧めします。 このセクションの方法のいずれかまたは両方など、セキュリティで保護された認証フローを使用します。
シークレット マネージャー ツール
サーバー プロジェクトが既に Secret Manager ツール用に初期化されている場合は、そのプロジェクト ファイル (<AppSecretsId>
) にアプリ シークレット識別子 (.csproj
) が既に存在します。 Visual Studio では、ソリューション エクスプローラーでプロジェクトが選択されているときに Properties パネルを見て、アプリ シークレット ID が存在するかどうかを確認できます。 アプリが初期化されていない場合は、サーバー プロジェクトのディレクトリに対して開かれたコマンド シェルで次のコマンドを実行します。 Visual Studio では、開発者 PowerShell コマンド プロンプトを使用できます (コマンド シェルを開いた後、 cd
コマンドを使用してディレクトリをサーバー プロジェクトに変更します)。
dotnet user-secrets init
Secret Manager ツールを使用して API キーを設定します。 次の例では、キー名はEmailAuthKey
と一致するようにAuthMessageSenderOptions.EmailAuthKey
され、キーは {KEY}
プレースホルダーで表されます。 API キーを使用して次のコマンドを実行します。
dotnet user-secrets set "EmailAuthKey" "{KEY}"
Visual Studio を使用している場合は、ソリューション エクスプローラーでサーバー プロジェクトを右クリックし、[ユーザー シークレットの管理] 選択することで、シークレットが設定されていることを確認。
詳細については、「ASP.NET Core での開発におけるアプリ シークレットの安全な保存」を参照してください。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。
Azure Key Vault
azure Key Vault は、アプリのクライアント シークレットをアプリに提供するための安全なアプローチを提供します。
キー コンテナーを作成してシークレットを設定するには、「Azure Key Vault シークレットについて (Azure ドキュメント)」を参照してください。Azure Key Vault の使用を開始するためにリソースをクロスリンクします。 このセクションのコードを実装するには、キー コンテナーとシークレットを作成するときに、Azure のキー コンテナー URI とシークレット名を記録します。 [アクセス ポリシー] パネルでシークレットのアクセス ポリシーを設定する場合:
- Get シークレット アクセス許可の取得のみが必要です。
- シークレットのプリンシパルとしてアプリケーションを選択します。
Azure または Entra ポータルで、電子メール プロバイダー キー用に作成したシークレットへのアクセス権がアプリに付与されていることを確認します。
重要
キー ボールト シークレットは、有効期限が設定された状態で作成されます。 キー ボルト シークレットの有効期限が切れるタイミングを追跡し、その日付が経過する前に、アプリ用の新しいシークレットを作成してください。
Microsoft Identity パッケージがまだアプリのパッケージ登録に含まれていない場合は、Azure Identity と Azure Key Vault のサーバー プロジェクトに次のパッケージを追加します。 これらのパッケージは Microsoft Identity Web パッケージによって推移的に提供されるため、アプリが Microsoft.Identity.Web
を参照していない場合にのみ追加する必要があります。
次の AzureHelper
クラスをサーバー プロジェクトに追加します。 GetKeyVaultSecret
メソッドは、キー ボールトからシークレットを取得します。 プロジェクトの名前空間スキームに合わせて名前空間 (BlazorSample.Helpers
) を調整します。
Helpers/AzureHelper.cs
:
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
{
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = tenantId,
SharedTokenCacheTenantId = tenantId
};
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
サービスがサーバー プロジェクトの Program
ファイルに登録されている場合は、Options 構成でシークレットを取得してバインドします。
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
var emailAuthKey = AzureHelper.GetKeyVaultSecret(
tenantId, vaultUri, "EmailAuthKey");
var authMessageSenderOptions =
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
.Bind(authMessageSenderOptions);
上記のコードが動作する環境を制御する場合 (たとえば、ローカル開発に Secret Manager ツール を使用することを選択したためにコードをローカルで実行しないようにする場合は、環境をチェックする条件付きステートメントで上記のコードをラップできます。
if (!context.HostingEnvironment.IsDevelopment())
{
...
}
サーバー プロジェクトの appsettings.json
の AzureAd
セクション (まだ存在しない場合は追加する必要がある場合があります) で、次の TenantId
および VaultUri
の構成キーと値を追加します (まだ存在しない場合)。
"AzureAd": {
"TenantId": "{TENANT ID}",
"VaultUri": "{VAULT URI}"
}
前の例では、次のようになります。
{TENANT ID}
プレースホルダーは、Azure のアプリのテナント ID です。{VAULT URI}
プレースホルダーは、キー ボールトの URI です。 URI に末尾のスラッシュを含めます。
例:
"TenantId": "00001111-aaaa-2222-bbbb-3333cccc4444",
"VaultUri": "https://contoso.vault.azure.net/"
構成は、アプリの環境構成ファイルに基づいて専用のキー コンテナーとシークレット名を簡単に指定するために使用されます。 たとえば、開発中の appsettings.Development.json
、ステージング時の appsettings.Staging.json
、運用デプロイの appsettings.Production.json
に対して、さまざまな構成値を指定できます。 詳細については、ASP.NET Core Blazor 構成を参照してください。
サーバー プロジェクトに IEmailSender
を実装する
次の例は、Mandrill.net を使用する Mailchimp のトランザクション API に基づいています。 別のプロバイダーについては、電子メール メッセージの送信を実装する方法に関するドキュメントを参照してください。
Mandrill.net NuGet パッケージをサーバー プロジェクトに追加します。
次の EmailSender
クラスを追加して、 IEmailSender<TUser>を実装します。 次の例では、 AppUser
は IdentityUserです。 メッセージ HTML マークアップをさらにカスタマイズできます。 message
に渡されるMandrillMessage
が<
文字で始まる限り、Mandrill.net API はメッセージ本文が HTML で構成されていることを前提としています。
EmailSender.cs
:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Mandrill;
using Mandrill.Model;
namespace Backend;
public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
ILogger<EmailSender> logger) : IEmailSender<AppUser>
{
private readonly ILogger logger = logger;
public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;
public Task SendConfirmationLinkAsync(AppUser user, string email,
string confirmationLink) => SendEmailAsync(email, "Confirm your email",
"<html lang=\"en\"><head></head><body>Please confirm your account by " +
$"<a href='{confirmationLink}'>clicking here</a>.</body></html>");
public Task SendPasswordResetLinkAsync(AppUser user, string email,
string resetLink) => SendEmailAsync(email, "Reset your password",
"<html lang=\"en\"><head></head><body>Please reset your password by " +
$"<a href='{resetLink}'>clicking here</a>.</body></html>");
public Task SendPasswordResetCodeAsync(AppUser user, string email,
string resetCode) => SendEmailAsync(email, "Reset your password",
"<html lang=\"en\"><head></head><body>Please reset your password " +
$"using the following code:<br>{resetCode}</body></html>");
public async Task SendEmailAsync(string toEmail, string subject, string message)
{
if (string.IsNullOrEmpty(Options.EmailAuthKey))
{
throw new Exception("Null EmailAuthKey");
}
await Execute(Options.EmailAuthKey, subject, message, toEmail);
}
public async Task Execute(string apiKey, string subject, string message,
string toEmail)
{
var api = new MandrillApi(apiKey);
var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail,
subject, message);
await api.Messages.SendAsync(mandrillMessage);
logger.LogInformation("Email to {EmailAddress} sent!", toEmail);
}
}
Note
メッセージの本文コンテンツには、メール サービス プロバイダーの特別なエンコードが必要な場合があります。 メッセージ本文のリンクを電子メール メッセージでフォローできない場合は、サービス プロバイダーのドキュメントを参照して問題のトラブルシューティングを行ってください。
次の IEmailSender<TUser> サービス登録をサーバー プロジェクトの Program
ファイルに追加します。
builder.Services.AddTransient<IEmailSender<AppUser>, EmailSender>();
電子メールの確認を要求するようにサーバー プロジェクトを構成する
サーバー プロジェクトの Program
ファイルで、アプリにサインインするために確認済みの電子メール アドレスが必要です。
AddIdentityCoreを呼び出す行を見つけて、RequireConfirmedEmail プロパティを true
に設定します。
- builder.Services.AddIdentityCore<AppUser>()
+ builder.Services.AddIdentityCore<AppUser>(o => o.SignIn.RequireConfirmedEmail = true)
クライアント プロジェクトのアカウント登録応答を更新する
クライアント プロジェクトの Register
コンポーネント (Components/Identity/Register.razor
) で、アカウント登録が成功したユーザーにメッセージを変更して、アカウントを確認するように指示します。 次の例には、サーバーで Identity をトリガーして確認メールを再送信するためのリンクが含まれています。
- You successfully registered and can <a href="login">login</a> to the app.
+ You successfully registered. You must now confirm your account by clicking
+ the link in the email that was sent to you. After confirming your account,
+ you can <a href="login">login</a> to the app.
+ <a href="resendConfirmationEmail">Resend confirmation email</a>
シード されたアカウントを確認するためにシード データ コードを更新する
サーバー プロジェクトのシード データ クラス (SeedData.cs
) で、 InitializeAsync
メソッドのコードを変更して、シードされたアカウントを確認します。そのため、いずれかのアカウントを使用してソリューションを実行するたびに電子メール アドレスの確認が不要になります。
- if (appUser is not null && user.RoleList is not null)
- {
- await userManager.AddToRolesAsync(appUser, user.RoleList);
- }
+ if (appUser is not null)
+ {
+ if (user.RoleList is not null)
+ {
+ await userManager.AddToRolesAsync(appUser, user.RoleList);
+ }
+
+ var token = await userManager.GenerateEmailConfirmationTokenAsync(appUser);
+ await userManager.ConfirmEmailAsync(appUser, token);
+ }
サイトにユーザーがいる用になった後でアカウントの確認を有効にする
ユーザーがいるサイトでアカウントの確認を有効にすると、すべての既存のユーザーがロックアウトされます。 既存のユーザーは、アカウントが確認されていないためにロックアウトされます。 この記事の範囲外である、次のいずれかの方法を使用します。
- データベースを更新して、既存のすべてのユーザーを確認済みとしてマークします。
- 既存のすべてのユーザーへの確認リンクを含む電子メールをバッチ送信します。この場合、各ユーザーは自分のアカウントを確認する必要があります。
パスワードの回復
パスワードの回復では、ユーザーにパスワード リセット コードを送信するために、サーバー アプリで電子メール プロバイダーを採用する必要があります。 そのため、この記事の前半のガイダンスに従って、電子メール プロバイダーを採用してください。
- サーバー プロジェクトの電子メール プロバイダーを選択して構成する
- 電子メール プロバイダーのセキュリティ キー のシークレットを構成する
- サーバー プロジェクトに
IEmailSender
を実装する
パスワードの回復は 2 段階のプロセスです。
- サーバー プロジェクト内の
/forgotPassword
によって提供されるMapIdentityApi エンドポイントに対して POST 要求が行われます。 UI のメッセージは、リセット コードの電子メールを確認するようにユーザーに指示します。 - ユーザーの電子メール アドレス、パスワード リセット コード、および新しいパスワードを使用して、サーバー プロジェクトの
/resetPassword
エンドポイントに対して POST 要求が行われます。
上記の手順は、 サンプル ソリューションの次の実装ガイダンスによって示されています。
クライアント プロジェクトで、次のメソッド シグネチャを IAccountManagement
クラス (Identity/IAccountManagement.cs
) に追加します。
public Task<bool> ForgotPasswordAsync(string email);
public Task<FormResult> ResetPasswordAsync(string email, string resetCode,
string newPassword);
クライアント プロジェクトで、上記のメソッドの実装を CookieAuthenticationStateProvider
クラス (Identity/CookieAuthenticationStateProvider.cs
) に追加します。
public async Task<bool> ForgotPasswordAsync(string email)
{
try
{
var result = await httpClient.PostAsJsonAsync(
"forgotPassword", new
{
email
});
if (result.IsSuccessStatusCode)
{
return true;
}
}
catch { }
return false;
}
public async Task<FormResult> ResetPasswordAsync(string email, string resetCode,
string newPassword)
{
string[] defaultDetail = [ "An unknown error prevented password reset." ];
try
{
var result = await httpClient.PostAsJsonAsync(
"resetPassword", new
{
email,
resetCode,
newPassword
});
if (result.IsSuccessStatusCode)
{
return new FormResult { Succeeded = true };
}
var details = await result.Content.ReadAsStringAsync();
var problemDetails = JsonDocument.Parse(details);
var errors = new List<string>();
var errorList = problemDetails.RootElement.GetProperty("errors");
foreach (var errorEntry in errorList.EnumerateObject())
{
if (errorEntry.Value.ValueKind == JsonValueKind.String)
{
errors.Add(errorEntry.Value.GetString()!);
}
else if (errorEntry.Value.ValueKind == JsonValueKind.Array)
{
errors.AddRange(
errorEntry.Value.EnumerateArray().Select(
e => e.GetString() ?? string.Empty)
.Where(e => !string.IsNullOrEmpty(e)));
}
}
return new FormResult
{
Succeeded = false,
ErrorList = problemDetails == null ? defaultDetail : [.. errors]
};
}
catch { }
return new FormResult
{
Succeeded = false,
ErrorList = defaultDetail
};
}
クライアント プロジェクトで、次の ForgotPassword
コンポーネントを追加します。
Components/Identity/ForgotPassword.razor
:
@page "/forgot-password"
@using System.ComponentModel.DataAnnotations
@using BlazorWasmAuth.Identity
@inject IAccountManagement Acct
<PageTitle>Forgot your password?</PageTitle>
<h1>Forgot your password?</h1>
<p>Provide your email address and select the <b>Reset password</b> button.</p>
<hr />
<div class="row">
<div class="col-md-4">
@if (!passwordResetCodeSent)
{
<EditForm Model="Input" FormName="forgot-password"
OnValidSubmit="OnValidSubmitStep1Async" method="post">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText @bind-Value="Input.Email"
id="Input.Email" class="form-control"
autocomplete="username" aria-required="true"
placeholder="name@example.com" />
<label for="Input.Email" class="form-label">
Email
</label>
<ValidationMessage For="() => Input.Email"
class="text-danger" />
</div>
<button type="submit" class="w-100 btn btn-lg btn-primary">
Request reset code
</button>
</EditForm>
}
else
{
if (passwordResetSuccess)
{
if (errors)
{
foreach (var error in errorList)
{
<div class="alert alert-danger">@error</div>
}
}
else
{
<div>
Your password was reset. You may <a href="login">login</a>
to the app with your new password.
</div>
}
}
else
{
<div>
A password reset code has been sent to your email address.
Obtain the code from the email for this form.
</div>
<EditForm Model="Reset" FormName="reset-password"
OnValidSubmit="OnValidSubmitStep2Async" method="post">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText @bind-Value="Reset.ResetCode"
id="Reset.ResetCode" class="form-control"
autocomplete="username" aria-required="true" />
<label for="Reset.ResetCode" class="form-label">
Reset code
</label>
<ValidationMessage For="() => Reset.ResetCode"
class="text-danger" />
</div>
<div class="form-floating mb-3">
<InputText type="password" @bind-Value="Reset.NewPassword"
id="Reset.NewPassword" class="form-control"
autocomplete="new-password" aria-required="true"
placeholder="password" />
<label for="Reset.NewPassword" class="form-label">
New Password
</label>
<ValidationMessage For="() => Reset.NewPassword"
class="text-danger" />
</div>
<div class="form-floating mb-3">
<InputText type="password"
@bind-Value="Reset.ConfirmPassword"
id="Reset.ConfirmPassword" class="form-control"
autocomplete="new-password" aria-required="true"
placeholder="password" />
<label for="Reset.ConfirmPassword" class="form-label">
Confirm Password
</label>
<ValidationMessage For="() => Reset.ConfirmPassword"
class="text-danger" />
</div>
<button type="submit" class="w-100 btn btn-lg btn-primary">
Reset password
</button>
</EditForm>
}
}
</div>
</div>
@code {
private bool passwordResetCodeSent, passwordResetSuccess, errors;
private string[] errorList = [];
[SupplyParameterFromForm(FormName = "forgot-password")]
private InputModel Input { get; set; } = new();
[SupplyParameterFromForm(FormName = "reset-password")]
private ResetModel Reset { get; set; } = new();
private async Task OnValidSubmitStep1Async()
{
passwordResetCodeSent = await Acct.ForgotPasswordAsync(Input.Email);
}
private async Task OnValidSubmitStep2Async()
{
var result = await Acct.ResetPasswordAsync(Input.Email, Reset.ResetCode,
Reset.NewPassword);
if (result.Succeeded)
{
passwordResetSuccess = true;
}
else
{
errors = true;
errorList = result.ErrorList;
}
}
private sealed class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; } = string.Empty;
}
private sealed class ResetModel
{
[Required]
[Base64String]
public string ResetCode { get; set; } = string.Empty;
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at " +
"max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string NewPassword { get; set; } = string.Empty;
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation " +
"password don't match.")]
public string ConfirmPassword { get; set; } = string.Empty;
}
}
終了Login
タグの直前にあるクライアント プロジェクトの Components/Identity/Login.razor
コンポーネント (</NotAuthorized>
) に、パスワードを忘れた場合のリンクを追加して、ForgotPassword
コンポーネントに到達します。
<div>
<a href="forgot-password">Forgot password</a>
</div>
メールとアクティビティのタイムアウト
非アクティブ状態の既定のタイムアウトは 14 日です。 サーバー プロジェクトでは、次のコードは、非アクティブタイムアウトを 5 日間に設定し、スライディング有効期限を設定します。
builder.Services.ConfigureApplicationCookie(options => {
options.ExpireTimeSpan = TimeSpan.FromDays(5);
options.SlidingExpiration = true;
});
すべての ASP.NET Core データ保護トークンの有効期限を変更する
サーバー プロジェクトでは、次のコードによって Data Protection トークンのタイムアウト期間が 3 時間に変更されます。
builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
options.TokenLifespan = TimeSpan.FromHours(3));
組み込みの Identity ユーザー トークン (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) には、1 日のタイムアウト があります。
Note
通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。
メール トークンの有効期間を変更する
Identity ユーザー トークンの既定のトークン有効期間は 1 日です。
Note
通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。
電子メール トークンの有効期間を変更するには、カスタム DataProtectorTokenProvider<TUser> を追加し、サーバー プロジェクトに DataProtectionTokenProviderOptions します。
CustomTokenProvider.cs
:
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
namespace BlazorSample;
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 class CustomPasswordResetTokenProvider<TUser>
: DataProtectorTokenProvider<TUser> where TUser : class
{
public CustomPasswordResetTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<PasswordResetTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<TUser>> logger)
: base(dataProtectionProvider, options, logger)
{
}
}
public class PasswordResetTokenProviderOptions :
DataProtectionTokenProviderOptions
{
public PasswordResetTokenProviderOptions()
{
Name = "PasswordResetDataProtectorTokenProvider";
TokenLifespan = TimeSpan.FromHours(3);
}
}
サーバー プロジェクトの Program
ファイルでカスタム トークン プロバイダーを使用するようにサービスを構成します。
builder.Services.AddIdentityCore<AppUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
new TokenProviderDescriptor(
typeof(CustomEmailConfirmationTokenProvider<AppUser>)));
options.Tokens.EmailConfirmationTokenProvider =
"CustomEmailConfirmation";
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services
.AddTransient<CustomEmailConfirmationTokenProvider<AppUser>>();
トラブルシューティング
メールが機能しない場合:
EmailSender.Execute
にブレークポイントを設定して、SendEmailAsync
が呼び出されることを確認します。- 問題をデバッグするために、
EmailSender.Execute
のようなコードを使用してメールを送信するコンソール アプリを作成します。 - メール プロバイダーの Web サイトでアカウントのメール履歴ページを確認します。
- メッセージがないか迷惑メール フォルダーを確認します。
- 別のメール プロバイダー (Microsoft、Yahoo、Gmail など) で別のメール エイリアスを試します。
- 別のメール アカウントに送信してみます。
その他のリソース
ASP.NET Core