カスタムユーザーデータを Identity ASP.NET Core プロジェクトに追加、ダウンロード、および削除するAdd, download, and delete custom user data to Identity in an ASP.NET Core project

作成者: Rick AndersonBy Rick Anderson

この記事では、次の方法について説明します。This article shows how to:

  • カスタムユーザーデータを ASP.NET Core web アプリに追加します。Add custom user data to an ASP.NET Core web app.
  • カスタムユーザーデータモデルを属性でマークして、 PersonalDataAttribute 自動的にダウンロードおよび削除できるようにします。Mark the custom user data model with the PersonalDataAttribute attribute so it's automatically available for download and deletion. データをダウンロードして削除できるようにすると、 GDPR の要件を満たすことができます。Making the data able to be downloaded and deleted helps meet GDPR requirements.

プロジェクトサンプルは、ページ web アプリから作成され Razor ますが、手順は ASP.NET CORE MVC web アプリの場合と似ています。The project sample is created from a Razor Pages web app, but the instructions are similar for a ASP.NET Core MVC web app.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

必須コンポーネントPrerequisites

Web アプリを作成する RazorCreate a Razor web app

  • Visual Studio の [ファイル] メニューから、 [新規作成] > [プロジェクト] の順に選択します。From the Visual Studio File menu, select New > Project . ダウンロードするサンプルコードの名前空間と一致させる場合は、プロジェクトに WebApp1 という名前を付けます。Name the project WebApp1 if you want to it match the namespace of the download sample code.
  • ASP.NET Core Web アプリケーション の選択- > OKSelect ASP.NET Core Web Application > OK
  • ドロップダウンで ASP.NET Core 3.0 を選択します。Select ASP.NET Core 3.0 in the dropdown
  • Web アプリケーション の選択- > OKSelect Web Application > OK
  • プロジェクトをビルドして実行します。Build and run the project.
  • Visual Studio の [ファイル] メニューから、 [新規作成] > [プロジェクト] の順に選択します。From the Visual Studio File menu, select New > Project . ダウンロードするサンプルコードの名前空間と一致させる場合は、プロジェクトに WebApp1 という名前を付けます。Name the project WebApp1 if you want to it match the namespace of the download sample code.
  • ASP.NET Core Web アプリケーション の選択- > OKSelect ASP.NET Core Web Application > OK
  • ドロップダウンで ASP.NET Core 2.2 を選択します。Select ASP.NET Core 2.2 in the dropdown
  • Web アプリケーション の選択- > OKSelect Web Application > OK
  • プロジェクトをビルドして実行します。Build and run the project.

Scaffolder を実行します。 IdentityRun the Identity scaffolder

  • ソリューションエクスプローラー で、プロジェクトを右クリックし、[ Add > New スキャフォールディング Item ] > ます。From Solution Explorer , right-click on the project > Add > New Scaffolded Item .
  • [ スキャフォールディングの追加 ] ダイアログボックスの左ペインで、[追加] を選択し Identity > Add ます。From the left pane of the Add Scaffold dialog, select Identity > Add .
  • [ 追加 Identity ] ダイアログで、次のオプションを選択します。In the Add Identity dialog, the following options:
    • 既存のレイアウト _Layout ファイルを選択し ます。Select the existing layout file ~/Pages/Shared/_Layout.cshtml
    • 上書きする以下のファイルを選択してください:Select the following files to override:
      • アカウント/登録Account/Register
      • アカウント/管理/インデックスAccount/Manage/Index
    • + 新しい データコンテキストクラス を作成するには、このボタンをクリックします。Select the + button to create a new Data context class . 型 (プロジェクトの名前が WebApp1 の場合は WebApp1Context ) をそのまま使用します。Accept the type ( WebApp1.Models.WebApp1Context if the project is named WebApp1 ).
    • + 新しい ユーザークラス を作成するには、このボタンを選択します。Select the + button to create a new User class . 型を受け入れます (プロジェクトの名前が WebApp1 の場合は WebApp1User ) > 追加 ] を使用します。Accept the type ( WebApp1User if the project is named WebApp1 ) > Add .
  • [追加] を選択します。Select Add .

移行、UseAuthentication、および layoutの指示に従って、次の手順を実行します。Follow the instruction in Migrations, UseAuthentication, and layout to perform the following steps:

  • 移行を作成し、データベースを更新します。Create a migration and update the database.
  • UseAuthenticationStartup.Configure に追加します。Add UseAuthentication to Startup.Configure.
  • <partial name="_LoginPartial" />レイアウトファイルにを追加します。Add <partial name="_LoginPartial" /> to the layout file.
  • アプリをテストします。Test the app:
    • ユーザーを登録するRegister a user
    • [ ログアウト ] リンクの横にある新しいユーザー名を選択します。Select the new user name (next to the Logout link). 場合によっては、ウィンドウを展開するか、ナビゲーションバーのアイコンを選択して、ユーザー名とその他のリンクを表示する必要があります。You might need to expand the window or select the navigation bar icon to show the user name and other links.
    • [ Personal Data ] タブを選択します。Select the Personal Data tab.
    • [ ダウンロード ] ボタンを選択し、ファイルの PersonalData.js を調べます。Select the Download button and examined the PersonalData.json file.
    • [ 削除 ] ボタンをテストします。これにより、ログオンしているユーザーが削除されます。Test the Delete button, which deletes the logged on user.

DB にカスタムユーザーデータを追加する IdentityAdd custom user data to the Identity DB

IdentityUserカスタムプロパティを使用して、派生クラスを更新します。Update the IdentityUser derived class with custom properties. プロジェクトに WebApp1 という名前を付けた場合、ファイルの名前は Areas/ Identity /Data/WebApp1User.cs になります。If you named the project WebApp1, the file is named Areas/Identity/Data/WebApp1User.cs . 次のコードを使用して、ファイルを更新します。Update the file with the following code:

using System;
using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data
{
    public class WebApp1User : IdentityUser
    {
        [PersonalData]
        public string Name { get; set; }
        [PersonalData]
        public DateTime DOB { get; set; }
    }
}
using Microsoft.AspNetCore.Identity;
using System;

namespace WebApp1.Areas.Identity.Data
{
    public class WebApp1User : IdentityUser
    {
        [PersonalData]
        public string Name { get; set; }
        [PersonalData]
        public DateTime DOB { get; set; }
    }
}

プロパティ は、次のよう になります。Properties with the PersonalData attribute are:

  • 区分/ Identity /Pages/Account/Manage/DeletePersonalData.cshtml ページがを呼び出したときに削除され Razor UserManager.Delete ます。Deleted when the Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml Razor Page calls UserManager.Delete.
  • ダウンロードされたデータには、[区分]、[ページ]、 および [ Identity Manage/Download] の各 ページで含まれ Razor ます。Included in the downloaded data by the Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml Razor Page.

Account/Manage/Index. cshtml ページを更新するUpdate the Account/Manage/Index.cshtml page

次の InputModel 強調表示されたコードを使用して、 区分/ Identity /Pages/Account/Manage/Index.cshtml.cs のを更新します。Update the InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs with the following highlighted code:

public partial class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    public string Username { get; set; }

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound(
                $"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound(
                $"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        if (!ModelState.IsValid)
        {
            await LoadAsync(user);
            return Page();
        }

        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
        if (Input.PhoneNumber != phoneNumber)
        {
            var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, 
                Input.PhoneNumber);

            if (!setPhoneResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException(
                    $"Unexpected error occurred setting phone number for user with ID '{userId}'.");
            }
        }
        
        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);

        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

次の強調表示されているマークアップを使用して、 区分//////////また Identity は更新プログラムを更新します。Update the Areas/Identity/Pages/Account/Manage/Index.cshtml with the following highlighted markup:

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" model="Model.StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Username"></label>
                <input asp-for="Username" class="form-control" disabled />
            </div>
            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.PhoneNumber"></label>
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <span asp-validation-for="Input.PhoneNumber" 
                    class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" 
                class="btn btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}
public partial class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly IEmailSender _emailSender;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
    }

    public string Username { get; set; }
    public bool IsEmailConfirmed { get; set; }

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        var userName = await _userManager.GetUserNameAsync(user);
        var email = await _userManager.GetEmailAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            Email = email,
            PhoneNumber = phoneNumber
        };

        IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);

        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        var email = await _userManager.GetEmailAsync(user);
        if (Input.Email != email)
        {
            var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
            if (!setEmailResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
            }
        }

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
        if (Input.PhoneNumber != phoneNumber)
        {
            var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
            if (!setPhoneResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'.");
            }
        }

        await _userManager.UpdateAsync(user);

        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }

    public async Task<IActionResult> OnPostSendVerificationEmailAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }


        var userId = await _userManager.GetUserIdAsync(user);
        var email = await _userManager.GetEmailAsync(user);
        var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
        var callbackUrl = Url.Page(
            "/Account/ConfirmEmail",
            pageHandler: null,
            values: new { userId = userId, code = code },
            protocol: Request.Scheme);
        await _emailSender.SendEmailAsync(
            email,
            "Confirm your email",
            $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

        StatusMessage = "Verification email sent. Please check your email.";
        return RedirectToPage();
    }
}

次の強調表示されているマークアップを使用して、 区分//////////また Identity は更新プログラムを更新します。Update the Areas/Identity/Pages/Account/Manage/Index.cshtml with the following highlighted markup:

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Username"></label>
                <input asp-for="Username" class="form-control" disabled />
            </div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                @if (Model.IsEmailConfirmed)
                {
                    <div class="input-group">
                        <input asp-for="Input.Email" class="form-control" />
                        <span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
                    </div>
                }
                else
                {
                    <input asp-for="Input.Email" class="form-control" />
                    <button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button>
                }
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.PhoneNumber"></label>
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Account/Register. cshtml ページを更新するUpdate the Account/Register.cshtml page

次の InputModel 強調表示されたコードを使用して、 区分/ Identity /Pages/Account/Register.cshtml.cs のを更新します。Update the InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs with the following highlighted code:

[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly UserManager<WebApp1User> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;

    public RegisterModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

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

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [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 Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public async Task OnGetAsync(string returnUrl = null)
    {
        ReturnUrl = returnUrl;
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        if (ModelState.IsValid)
        {
            var user = new WebApp1User {
                Name = Input.Name,
                DOB = Input.DOB,
                UserName = Input.Email, 
                Email = Input.Email 
            };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = user.Id, code = code },
                    protocol: Request.Scheme);

                await _emailSender.SendEmailAsync(Input.Email, 
                    "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                }
                else
                {
                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
            }
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

次の強調表示されたマークアップを使用して、 区分//[///////////////また Identity はのUpdate the Areas/Identity/Pages/Account/Register.cshtml with the following highlighted markup:

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>

            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
                <span asp-validation-for="Input.DOB" class="text-danger"></span>
            </div>
            
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>Use another service to register.</h4>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 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 id="external-account" asp-page="./ExternalLogin" 
                        asp-route-returnUrl="@Model.ReturnUrl" method="post" 
                        class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" 
                                        value="@provider.Name" 
                                        title="Log in using your @provider.DisplayName account">
                                            @provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}
[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly UserManager<WebApp1User> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;

    public RegisterModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [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 Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public void OnGet(string returnUrl = null)
    {
        ReturnUrl = returnUrl;
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        if (ModelState.IsValid)
        {
            var user = new WebApp1User {
                Name = Input.Name,
                DOB = Input.DOB,
                UserName = Input.Email,
                Email = Input.Email
            };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { userId = user.Id, code = code },
                    protocol: Request.Scheme);

                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

次の強調表示されたマークアップを使用して、 区分//[///////////////また Identity はのUpdate the Areas/Identity/Pages/Account/Register.cshtml with the following highlighted markup:

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>

            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
                <span asp-validation-for="Input.DOB" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

プロジェクトをビルドします。Build the project.

カスタムユーザーデータの移行を追加するAdd a migration for the custom user data

Visual Studio パッケージマネージャーコンソール で次のようにします。In the Visual Studio Package Manager Console :

Add-Migration CustomUserData
Update-Database

テストの作成、表示、ダウンロード、カスタムユーザーデータの削除Test create, view, download, delete custom user data

アプリをテストします。Test the app:

  • 新しいユーザーを登録します。Register a new user.
  • ページ上のカスタムユーザーデータを表示し /Identity/Account/Manage ます。View the custom user data on the /Identity/Account/Manage page.
  • ページからユーザーの個人データをダウンロードして表示し /Identity/Account/Manage/PersonalData ます。Download and view the users personal data from the /Identity/Account/Manage/PersonalData page.

IdentityIUserClaimsPrincipalFactory を使用してにクレームを追加するAdd claims to Identity using IUserClaimsPrincipalFactory

注意

このセクションは、前のチュートリアルの拡張機能ではありません。This section isn't an extension of the previous tutorial. チュートリアルを使用してビルドされたアプリに次の手順を適用するには、 GitHub の問題を参照してください。To apply the following steps to the app built using the tutorial, see this GitHub issue.

インターフェイスを使用して、追加の要求をに追加でき ASP.NET Core Identity IUserClaimsPrincipalFactory<T> ます。Additional claims can be added to ASP.NET Core Identity by using the IUserClaimsPrincipalFactory<T> interface. このクラスは、メソッドでアプリに追加でき Startup.ConfigureServices ます。This class can be added to the app in the Startup.ConfigureServices method. 次のように、クラスのカスタム実装を追加します。Add the custom implementation of the class as follows:

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

    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, 
        AdditionalUserClaimsPrincipalFactory>();

デモコードでは、クラスを使用し ApplicationUser ます。The demo code uses the ApplicationUser class. このクラス IsAdmin は、追加の要求を追加するために使用されるプロパティを追加します。This class adds an IsAdmin property which is used to add the additional claim.

public class ApplicationUser : IdentityUser
{
    public bool IsAdmin { get; set; }
}

AdditionalUserClaimsPrincipalFactory は、UserClaimsPrincipalFactory インターフェイスを実装します。The AdditionalUserClaimsPrincipalFactory implements the UserClaimsPrincipalFactory interface. 新しいロールクレームがに追加され ClaimsPrincipal ます。A new role claim is added to the ClaimsPrincipal.

public class AdditionalUserClaimsPrincipalFactory 
        : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public AdditionalUserClaimsPrincipalFactory( 
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager, 
        IOptions<IdentityOptions> optionsAccessor) 
        : base(userManager, roleManager, optionsAccessor)
    {}

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);
        var identity = (ClaimsIdentity)principal.Identity;

        var claims = new List<Claim>();
        if (user.IsAdmin)
        {
            claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
        }
        else
        {
            claims.Add(new Claim(JwtClaimTypes.Role, "user"));
        }

        identity.AddClaims(claims);
        return principal;
    }
}

その後、追加の要求をアプリで使用できます。The additional claim can then be used in the app. ページでは、 Razor IAuthorizationService インスタンスを使用して要求値にアクセスできます。In a Razor Page, the IAuthorizationService instance can be used to access the claim value.

@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService

@if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
    <ul class="mr-auto navbar-nav">
        <li class="nav-item">
            <a class="nav-link" asp-controller="Admin" asp-action="Index">ADMIN</a>
        </li>
    </ul>
}