Introdução ao Identity no ASP.NET Core

De Rick Anderson

ASP.NET Core Identity:

  • É uma API que suporta a funcionalidade de logon da interface do usuário (UI).
  • Gerencia usuários, senhas, dados de perfil, funções, declarações, tokens, confirmação por email e muito mais.

Os usuários podem criar uma conta com as informações de logon armazenadas em Identity ou podem usar um provedor de logon externo. Os provedores de logon externo suportados incluem Facebook, Google, conta Microsoft e Twitter.

Para obter informações sobre como exigir que todos os usuários sejam autenticados globalmente, confira Exigir usuários autenticados.

O código-fonte Identity está disponível no GitHub. Faça Scaffold Identity e exiba os arquivos gerados para analisar a interação do modelo com Identity.

Normalmente, o Identity é configurado usando um banco de dados do SQL Server para armazenar nomes de usuários, senhas e dados de perfil. Como alternativa, você pode usar outro armazenamento persistente, por exemplo, o Armazenamento de Tabelas do Azure.

Neste tópico, você aprenderá a usar Identity para registrar, fazer o logon e fazer o logoff de um usuário. Observação: os modelos tratam o nome de usuário e o email como os mesmos para os usuários. Para obter instruções mais detalhadas sobre como criar aplicativos que usam Identity, confira Próximas Etapas.

A Identity do ASP.NET Core não está relacionada à plataforma de identity da Microsoft. A plataforma de identity da Microsoft é:

  • Uma evolução da plataforma de desenvolvedor do Azure Active Directory (Azure AD).
  • Uma solução de identity alternativa para autenticação e autorização em aplicativos do ASP.NET Core.

O ASP.NET Core Identity adiciona a funcionalidade de logon da interface do usuário aos aplicativos Web do ASP.NET Core. Para proteger APIs Web e SPAs, use uma das seguintes opções:

O Duende Identity Server é uma estrutura do OpenID Connect e OAuth 2.0 para ASP.NET Core. O Duende Identity Server habilita os seguintes recursos de segurança:

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

O Software Duende pode exigir que você pague uma taxa de licença pelo uso de produção do Duende Identity Server. Para obter mais informações, consulte Migrar do ASP.NET Core 5.0 para o 6.0.

Para obter mais informações, confira a documentação do Duende Identity Server (site da Duende Software).

Exiba ou faça download do código de exemplo (como fazer download).

Criar um aplicativo Web com autenticação

Crie um projeto de aplicativo Web ASP.NET Core com contas de usuário individuais.

  • Selecione o modelo Aplicativo Web ASP.NET Core. Dê ao projeto o nome WebApp1 para que ele tenha o mesmo namespace que o download do projeto. Clique no OK.
  • Na entrada de Tipo de autenticação, selecione Contas de Usuário Individuais.

O projeto gerado fornece o ASP.NET Core Identity como uma biblioteca de classes do Razor. A biblioteca de classes IdentityRazor expõe os pontos de extremidade com a área Identity. Por exemplo:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Aplicar migrações

Aplique as migrações para inicializar o banco de dados.

Execute o seguinte comando no Console do Gerenciador de Pacotes (PMC):

Update-Database

Registro de teste e logon

Execute o aplicativo e registre um usuário. Dependendo do tamanho da tela, talvez você precise selecionar o botão de alternância de navegação para conferir os links Registrar e Logon.

Exibição do banco de dados Identity

  • No menu Exibir, selecione Pesquisador de Objetos do Servidor SQL. (SSOX).
  • Navegue até (localdb)MSSQLLocalDB(SQL Server 13). Clique com o botão direito do mouse em dbo.AspNetUsers>Dados de Exibição:

Menu contextual na tabela AspNetUsers no Pesquisador de Objetos do SQL Server

Configurar os serviços Identity

Os serviços foram adicionados em Program.cs. O padrão típico é chamar os métodos na seguinte ordem:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

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

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

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

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();

O código anterior configura Identity com valores da opção padrão. Os serviços são disponibilizados para o aplicativo por meio da injeção de dependência.

Identity é habilitado quando você chama UseAuthentication. UseAuthentication adiciona autenticação Middleware ao pipeline de solicitações.

O aplicativo gerado pelo modelo não usa a autorização. app.UseAuthorization é incluído para que você certifique-se de que ele seja adicionado na ordem correta caso o aplicativo adicione uma autorização. UseRouting, UseAuthentication e UseAuthorization devem ser chamados na ordem mostrada no código anterior.

Para obter mais informações sobre IdentityOptions, confira IdentityOptions e a Inicialização do aplicativo.

Registro de Scaffold, Logon, Logoff e ConfirmaçãoDeRegistro

Adicione os arquivos Register, Login, LogOut e RegisterConfirmation. Siga as instruções identity do Scaffold em um projeto do Razor com autorização para gerar o código mostrado nesta seção.

Examinar o Registro

Quando um usuário clica no botão Registrar na página Register, a ação RegisterModel.OnPostAsync é chamada. O usuário foi criado por CreateAsync(TUser) no objeto _userManager:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { 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();
}

Para desabilitar a verificação padrão da conta

Com os modelos padrão, o usuário é redirecionado para Account.RegisterConfirmation, no qual pode selecionar um link para confirmar a conta. O padrão Account.RegisterConfirmation é usado apenas para teste. A verificação automática de conta deve ser desabilitada em um aplicativo de produção.

Para exigir uma conta confirmada e impedir o logon imediato no registro, defina DisplayConfirmAccountLink = false em /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

[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;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

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

        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();
    }
}

Fazer logon

O formulário de Logon é exibido quando:

  • O link Logon é selecionado.
  • Um usuário tenta acessar uma página restrita que não está autorizado a acessar ou quando não tiver sido autenticado pelo sistema.

Quando o formulário da página de logon é enviado, a ação OnPostAsync é chamada. PasswordSignInAsync é chamada no objeto _signInManager.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

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

Para obter informações sobre como tomar decisões sobre autorização, confira Introdução à autorização no ASP.NET Core.

Faça logoff

O link de Logoff invoca a ação LogoutModel.OnPost.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

No código anterior, o código return RedirectToPage(); precisa ser um redirecionamento para que o navegador realize uma nova solicitação e a identity do usuário seja atualizada.

SignOutAsync limpa as declarações do usuário armazenadas em cookie.

A postagem é especificada em Pages/Shared/_LoginPartial.cshtml:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Teste Identity

Os modelos padrão de projetos Web permitem o acesso anônimo às home pages. Para testar Identity, adicione [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Se estiver conectado, desconecte-se. Execute o aplicativo e selecione o link Privacy. Você é redirecionado à página de logon.

Explorar Identity

Para explorar Identity em mais detalhes:

Identity Componentes

Todos os pacotes NuGet dependentes de Identity estão incluídos na estrutura compartilhada do ASP.NET Core.

O principal pacote para Identity é Microsoft.AspNetCore.Identity. Esse pacote contém o principal conjunto de interfaces para o ASP.NET Core Identity e é incluído por Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migrando para o ASP.NET Core Identity

Para obter mais informações e diretrizes sobre como migrar o armazenamento existente Identity, confira Migrar a autenticação e Identity.

Como definir a força da senha

Confira a Configuração para ver um exemplo que define os requisitos mínimos de senha.

AddDefaultIdentity e AddIdentity

AddDefaultIdentity foi introduzido no ASP.NET Core 2.1. Chamar AddDefaultIdentity é semelhante a chamar o seguinte:

Para obter mais informações, consulte Fonte AddDefaultIdentity.

Impedir a publicação de ativos estáticos do Identity

Para impedir a publicação de ativos estáticos Identity (folhas de estilo e arquivos JavaScript para a interface do usuário Identity) na raiz da Web, adicione a seguinte propriedade ResolveStaticWebAssetsInputsDependsOn e destino RemoveIdentityAssets ao arquivo do projeto do aplicativo:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Próximas etapas

De Rick Anderson

ASP.NET Core Identity:

  • É uma API que suporta a funcionalidade de logon da interface do usuário (UI).
  • Gerencia usuários, senhas, dados de perfil, funções, declarações, tokens, confirmação por email e muito mais.

Os usuários podem criar uma conta com as informações de logon armazenadas em Identity ou podem usar um provedor de logon externo. Os provedores de logon externo suportados incluem Facebook, Google, conta Microsoft e Twitter.

Para obter informações sobre como exigir que todos os usuários sejam autenticados globalmente, confira Exigir usuários autenticados.

O código-fonte Identity está disponível no GitHub. Faça Scaffold Identity e exiba os arquivos gerados para analisar a interação do modelo com Identity.

Normalmente, o Identity é configurado usando um banco de dados do SQL Server para armazenar nomes de usuários, senhas e dados de perfil. Como alternativa, você pode usar outro armazenamento persistente, por exemplo, o Armazenamento de Tabelas do Azure.

Neste tópico, você aprenderá a usar Identity para registrar, fazer o logon e fazer o logoff de um usuário. Observação: os modelos tratam o nome de usuário e o email como os mesmos para os usuários. Para obter instruções mais detalhadas sobre como criar aplicativos que usam Identity, confira Próximas Etapas.

A plataforma de identity da Microsoft é:

  • Uma evolução da plataforma de desenvolvedor do Azure Active Directory (Azure AD).
  • Uma solução de identity alternativa para autenticação e autorização em aplicativos do ASP.NET Core.
  • Não está relacionado ao ASP.NET Core Identity.

O ASP.NET Core Identity adiciona a funcionalidade de logon da interface do usuário aos aplicativos Web do ASP.NET Core. Para proteger APIs Web e SPAs, use uma das seguintes opções:

O Duende IdentityServer é uma estrutura do OpenID Connect e OAuth 2.0 para ASP.NET Core. O IdentityServer da Duende habilita os seguintes recursos de segurança:

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Para obter mais informações, consulte Visão geral do IdentityServer da Duende.

Para obter mais informações sobre outros provedores de autenticação, consulte Opções de autenticação de OSS da comunidade para ASP.NET Core

Exiba ou faça download do código de exemplo (como fazer download).

Criar um aplicativo Web com autenticação

Crie um projeto de aplicativo Web ASP.NET Core com contas de usuário individuais.

  • Selecione Arquivo>Novo>Projeto.
  • Selecione Aplicativo Web ASP.NET Core. Dê ao projeto o nome WebApp1 para que ele tenha o mesmo namespace que o download do projeto. Clique no OK.
  • Selecione um aplicativo Web ASP.NET Core e, em seguida, selecione Alterar autenticação.
  • Selecione Contas de usuário individuais e clique em OK.

O projeto gerado fornece o ASP.NET Core Identity como uma biblioteca de classes do Razor. A biblioteca de classes IdentityRazor expõe os pontos de extremidade com a área Identity. Por exemplo:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Aplicar migrações

Aplique as migrações para inicializar o banco de dados.

Execute o seguinte comando no Console do Gerenciador de Pacotes (PMC):

PM> Update-Database

Registro de teste e logon

Execute o aplicativo e registre um usuário. Dependendo do tamanho da tela, talvez você precise selecionar o botão de alternância de navegação para conferir os links Registrar e Logon.

Exibição do banco de dados Identity

  • No menu Exibir, selecione Pesquisador de Objetos do Servidor SQL. (SSOX).
  • Navegue até (localdb)MSSQLLocalDB(SQL Server 13). Clique com o botão direito do mouse em dbo.AspNetUsers>Dados de Exibição:

Menu contextual na tabela AspNetUsers no Pesquisador de Objetos do SQL Server

Configurar os serviços Identity

Os serviços foram adicionados em ConfigureServices. O padrão típico consiste em chamar todos os métodos Add{Service} e, em seguida, chamar todos os métodos services.Configure{Service}.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

O código destacado anteriormente configura Identity com valores de opção padrão. Os serviços são disponibilizados para o aplicativo por meio da injeção de dependência.

Identity é habilitado quando você chama UseAuthentication. UseAuthentication adiciona autenticação Middleware ao pipeline de solicitações.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

O código anterior configura Identity com valores da opção padrão. Os serviços são disponibilizados para o aplicativo por meio da injeção de dependência.

Identity é habilitado quando você chama UseAuthentication. UseAuthentication adiciona autenticação Middleware ao pipeline de solicitações.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

O aplicativo gerado pelo modelo não usa a autorização. app.UseAuthorization é incluído para que você certifique-se de que ele seja adicionado na ordem correta caso o aplicativo adicione uma autorização. UseRouting, UseAuthentication, UseAuthorization e UseEndpoints devem ser chamados na ordem mostrada no código anterior.

Para obter mais informações sobre IdentityOptions e Startup, confira IdentityOptions e Inicialização do aplicativo.

Registro de Scaffold, Logon, Logoff e ConfirmaçãoDeRegistro

Adicione os arquivos Register, Login, LogOut e RegisterConfirmation. Siga as instruções identity do Scaffold em um projeto do Razor com autorização para gerar o código mostrado nesta seção.

Examinar o Registro

Quando um usuário clica no botão Registrar na página Register, a ação RegisterModel.OnPostAsync é chamada. O usuário foi criado por CreateAsync(TUser) no objeto _userManager:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { 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();
}

Para desabilitar a verificação padrão da conta

Com os modelos padrão, o usuário é redirecionado para Account.RegisterConfirmation, no qual pode selecionar um link para confirmar a conta. O padrão Account.RegisterConfirmation é usado apenas para teste. A verificação automática de conta deve ser desabilitada em um aplicativo de produção.

Para exigir uma conta confirmada e impedir o logon imediato no registro, defina DisplayConfirmAccountLink = false em /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

[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;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

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

        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();
    }
}

Fazer logon

O formulário de Logon é exibido quando:

  • O link Logon é selecionado.
  • Um usuário tenta acessar uma página restrita que não está autorizado a acessar ou quando não tiver sido autenticado pelo sistema.

Quando o formulário da página de logon é enviado, a ação OnPostAsync é chamada. PasswordSignInAsync é chamada no objeto _signInManager.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

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

Para obter informações sobre como tomar decisões sobre autorização, confira Introdução à autorização no ASP.NET Core.

Faça logoff

O link de Logoff invoca a ação LogoutModel.OnPost.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

No código anterior, o código return RedirectToPage(); precisa ser um redirecionamento para que o navegador realize uma nova solicitação e a identity do usuário seja atualizada.

SignOutAsync limpa as declarações do usuário armazenadas em cookie.

A postagem é especificada em Pages/Shared/_LoginPartial.cshtml:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Teste Identity

Os modelos padrão de projetos Web permitem o acesso anônimo às home pages. Para testar Identity, adicione [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Se estiver conectado, desconecte-se. Execute o aplicativo e selecione o link Privacy. Você é redirecionado à página de logon.

Explorar Identity

Para explorar Identity em mais detalhes:

Identity Componentes

Todos os pacotes NuGet dependentes de Identity estão incluídos na estrutura compartilhada do ASP.NET Core.

O principal pacote para Identity é Microsoft.AspNetCore.Identity. Esse pacote contém o principal conjunto de interfaces para o ASP.NET Core Identity e é incluído por Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migrando para o ASP.NET Core Identity

Para obter mais informações e diretrizes sobre como migrar o armazenamento existente Identity, confira Migrar a autenticação e Identity.

Como definir a força da senha

Confira a Configuração para ver um exemplo que define os requisitos mínimos de senha.

Impedir a publicação de ativos estáticos do Identity

Para impedir a publicação de ativos estáticos Identity (folhas de estilo e arquivos JavaScript para a interface do usuário Identity) na raiz da Web, adicione a seguinte propriedade ResolveStaticWebAssetsInputsDependsOn e destino RemoveIdentityAssets ao arquivo do projeto do aplicativo:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Próximas etapas