ASP.NET Identity – Customizando o cadastro de usuários

Com o lançamento do ASP.NET MVC 5 um novo modelo de autenticação e autorização foi disponibilizado, o ASP.NET Identity, uma solução muito mais aberta e granular, acompanhe aqui como efetuar algumas customizações.

O ASP.NET Identity pode ser configurado no momento da criação da aplicação web, onde algumas opções são disponibilizadas para sua utilização, neste exemplo utilizarei o modelo de Individual User Accounts (modelo padrão).

ASP.NET Identity

O template de projeto que o Visual Studio disponibiliza na criação da aplicação ASP.NET MVC contempla uma implementação do ASP.NET Identity da qual já é possivel cadastrar-se como usuário. Ao executarmos o projeto pela primeira vez e clicar no link Register no topo da aplicação, será exibida a View de registro.

ASP.NET Identity

Até este ponto mais fácil seria impossível certo?
Suponha que o seu cadastro necessite de mais algumas informações como:

  • Nome
  • Sobrenome
  • E-mail

O ASP.NET Identity expõe um conjunto de classes de forma que fica muito fácil realizar qualquer tipo de customização. Mãos à obra.

Passo 1 – Customizar a classe de usuário

Localize e abra o arquivo IdentityModels.cs na pasta Models do seu projeto ASP.NET MVC. O código original será este:

namespace DemoIdentity.Models
{
    public class ApplicationUser : IdentityUser
    {
    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

Observe que existem duas classes nesse arquivo (depois refatoramos) a classe ApplicationUser é sua classe de usuário, note que ela não possui propriedade nenhuma apenas herda de IdentityUser, pois é esta classe que possui as propriedades de usuário, a classe ApplicationUser está ali disponível justamente para a customização.

Abaixo o código após a customização

namespace DemoIdentity.Models
{
    public class ApplicationUser : IdentityUser
    {
        public string Nome { get; set; }

        public string Sobrenome { get; set; }

        public string Email { get; set; }
    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

Passo 2 – Customizar a ViewModel

Neste projeto está sendo utilizado o padrão ViewModel, isso significa que a View de registro é baseada nesta ViewModel para exibir as informações em tela.

Localize e abra o arquivo AccountViewModels.cs na pasta Models, originalmente existem quatro classes neste mesmo arquivo (mais um item para refatorar). Encontre a classe RegisterViewModel:

public class RegisterViewModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} 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; }
}

A mesma classe agora após a customização:

public class RegisterViewModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    public string Nome { get; set; }

    [Required]
    public string Sobrenome { get; set; }

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

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} 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; }
}

Note que fiz uso de alguns DataAnnotations para configurar que os campos sejam requeridos e para ocorrer a validação de formato de email durante o input dos dados.

Passo 3 – Customizar a View

Agora para preencher com dados as novas propriedades da ViewModel será necessário customizar a View.

Localize e abra o arquivo Register.cshtml na pasta Views > Account.
Note que a View faz uso da ViewModel que acabamos de customizar

@model DemoIdentity.Models.RegisterViewModel

O resultado final da View será esse:

@model DemoIdentity.Models.RegisterViewModel
@{
    ViewBag.Title = "Register";
}

<h2>@ViewBag.Title.</h2>

@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>Create a new account.</h4>
    <hr />
    @Html.ValidationSummary()
    <div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.Nome, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Nome, new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.Sobrenome, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Sobrenome, new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Register" />
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Execute a aplicação para validar o resultado:

ASP.NET Identity

Passo 4 – Customizar a Controller

Este é o passo final, apesar da navegação estar pronta, a Controller ainda não está apta a receber as novas propriedades customizadas e realizar a gravação no banco.

Localize e abra o arquivo AccountController.cs na pasta Controllers
Identifique a Action de registro, o código original será este:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser() { UserName = model.UserName };
        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            await SignInAsync(user, isPersistent: false);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            AddErrors(result);
        }
    }

    return View(model);
}

Após a customização o resultado deverá ser este:

public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser()
        {
            UserName = model.UserName,
            Nome = model.Nome,
            Sobrenome = model.Sobrenome,
            Email = model.Email
        };

        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            await SignInAsync(user, isPersistent: false);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            AddErrors(result);
        }
    }

    return View(model);
}

Note que esta Action recebe o objeto RegisterViewModel como parâmetro, foi necessário apenas inserir as novas propriedades do objeto ApplicationUser que receberão as informações que vieram da ViewModel.

Sua customização está finalizada, execute a aplicação, registre um usuário e confira o resultado.

Observações:

  • O primeiro cadastro pode demorar um pouco, pois o mecanismo do Code First está criando o banco de dados e as tabelas necessárias para o ASP.NET Identity operar.
  • Faça testes no formulário, por ex, deixar algum campo em branco ou usar um e-mail em formato inválido.

Após registrar o usuário pela aplicação, você pode conferir o resultado da criação do banco de dados através do painel SQL Server Object Explorer do Visual Studio:

ASP.NET Identity

Vimos que é muito fácil realizar uma customização de um controle de acesso pelo ASP.NET Identity, caso queira criar mais campos basta seguir o mesmo roteiro.

Referências

Em breve publicarei mais artigos de customização do ASP.NET Identity, com técnicas mais avançadas e refatoração do projeto original.

Ficou com dúvidas? Quer compartilhar conosco alguma experiência? Utilize os comentários abaixo 😉

Até a próxima.

61 pensou em “ASP.NET Identity – Customizando o cadastro de usuários

  1. Fala Edu.
    Cara parabéns pelo seu blog.
    Estive no VS Summit 2014 e fiz questão de assistir sua palestra sobre as novidades do MVC 5.1.
    O Identity me trouxe muitos problemas no inicio dos meu trabalhos com MVC :/ .
    Procurei em vários lugares, algo para poder customizar ele por completo, e não havia encontrado nada que exemplificasse a simplicidade de tal customização.
    Apanhei muito no inicio até entender, mas, consegui.
    Mas mesmo assim eu não alterei o Identity criado, e sim, fiz do zero, dando override nos métodos necessários e assim por diante, eu comparo a construção de uma casa rs.
    Gostei muito dessa sua iniciativa, já que o conteúdo que eu encontrei e que me ajudou não é em português e muito menos uma alteração rs.
    Se vê muito pouco ajuda desse tipo, principalmente customizando o Idenity para trabalhar com Usuários, Perfis e Permissões.

    Muito bom,
    Abraço e sucesso.

    • Olá Fernando!

      Muito obrigado pelo feedback!!!

      Apanhei um pouco dos curtos 30 minutos e do projetor, vim com muita bagagem para entregar hehe, que bom que gostou.
      Eu gravei um vídeo de 1h00 reproduzindo a palestra! Pode consultar no meu último post.

      Sobre o ASP.NET Identity realmente não temos conteúdo, aguarde por mais, vou falar de uso e customização de Claims, capturar Claims do Facebook e criar SSO entre outras features, quero mapear todas as situações com o Identity 🙂

      Tendo dúvidas posta ai ou procura a gente lá no grupo ASP.NET MVC pt-br no FB.

      Abraços.

  2. E se eu herdar o ApplicationUser em minha classe? Não poderia? assim poderia ter o meu usuário do sistema com qualquer atributo a mais…

    Na verdade não sei, e nem testei, mas teria um sentido né ? rs

    • Os atributos a mais vc escreve da forma que aprentei no artigo, seria o caminho mais curto.

      Mas existem N formas que vc poderia extender o ASP.NET Identity, depende do que precisa.

      • Pois é, a minha ideia seria ter várias classes do tipo “IdentityUser” pois eu teria vários tipos de usuários que fariam login…rsrsrs
        Eu herdei direto da classe Identity, mas ao tentar login ele não acha.
        Mas irei tentando, obrigado pelo artigo Eduardo.

  3. Ótimo artigo Eduardo! Parabens!

    Gostaria de sanar uma dúvida. Vejo o uso do ASP.NET MVC um framework forte em questão de produtividade e muitos dos componentes como no caso do identity são ligados ao Entity Framework.

    Tenho muita vontade de implementar eles aqui na empresa mas o grande problema é o uso do Entity Framework pois os seniors aqui são spartanos e fazem tudo a mão na base de procedures SQL Server 2012 além de que ainda usam muito Web Forms aqui, estamos ainda no processo de adaptação para migrar ao MVC, mas a batalha ainda está longe para tornar realidade. Há apenas 1 projeto para ser usado MVC.

    Ai eu te pergunto, existe alguma forma de usar o identity sem o uso do EF? Ou usar o ASP.Net Identity sem o EF?

    Conhece alguma forma?

    Obrigado pela atenção! Estou adorando os artigos.

    • Olá Rodrigues, obrigado pelo feedback!

      Você pode utilizar ASP.NET Identity no WebForms e você também pode escolher outros providers para o Identity, por exemplo NHibernate, (é um ORM assim como o EF), você pode utilizar o Dapper (Micro ORM) e etc… Agora para utilizar procedures e ADO puro, você precisaria customizar muito do Identity para isso, não sei se vale a pena para vocês.

      Vamos trabalhar na mente dos veteranos para mostrar que as coisas mudaram, a roda foi reescrita 😉

      Abs!

  4. Ótimo artigo Eduardo!

    Pergunta sobre Task e async, qual o objetivo de ser async seu método porém com await no retorno? Pergunto pois se é async deveria ser totalmente assíncrono ou essa utilização está se tornando padrão para que o framework consiga manipular de uma maneira melhor a task?

    Abs

  5. Olá Eduardo Pires, muito bom esse artigo. Tenho uma dúvida, com o asp.net identity é possível eu criar uma conta com acesso temporário? Por exemplo preciso que um usuário consiga acessar o sistema por apenas 7 dias, depois desse prazo a conta é bloqueada automaticamente.

    Abraços.

    • Olá Kassio!

      Obrigado pelo feedback, é possível sim, mas o Identity não sabe trabalhar dessa forma, o que você precisa fazer é utilizar ele da forma normal e deixar um processo rodando para inativar esses usuários após 7 dias de cadastro.

      Abs!

  6. Eduardo, ótimo artigo!

    E se eu tiver uma classe de Usuario e quiser realizar login com ela como que eu faço?

    Abs!

  7. Eduardo, tenho a seguinte situação:

    Possuo dois tipos de usuários o Usuário Convencional (UserEntity) e o
    Administrador do Sistema (AdminUserEntity). O UserEntity vai acessar o site normal (1 projeto MVC) e o Administrador irá acessar outro site (+1 projeto MVC). Sabe me dar alguma luz de como realizar a implementação deste caso com o Identity?

    – Ter dois IdentityDbContext (um para cada usuário)?

  8. Eduardo, parabéns. O seu artigo é muito bom.

    No meu caso, eu preciso alterar o nome das tabelas, para se adequar ao padrão utilizado na minha empresa. É possível? Pode me dar um exemplo, por favor?

    Obrigado.

  9. Olá Eduardo Pires
    Assisti o teu vídeo sobre o identity, ainda não digeri tudo, gostei bastante da parte em que falaste do claims. Gostaria de saber como utilizar o identity com o Windows Authentication e depois utilizar roles ou claims para permitir ou não o utilizador (usuário) de aceder a determinadas tarefas do programa.

    • É bem simples, vc pode até configurar o Windows Authentication no momento da criação da aplicação conforme eu mostro no vídeo e durante o login vc pode ler os claims de alguma base e associar ao usuário.

      Abs!

      • Olá Eduardo Pires,

        Eu tenho a mesma dúvida do Joaquim (existem tantos vídeos em toda a web sobre o Identity, mas nenhum faz a sua construção usando o Windows Authentication :D)
        Quando diz: “…durante o login…”, mas com o “Windows Authentication” este formulário é feito de forma automática pelo site, ou seja, só se eu fizer o overload do AuthorizeAttribute para apanhar esta ação de login e então ler as claims da DB, é isto?

  10. “Em breve publicarei mais artigos de customização do ASP.NET Identity, com técnicas mais avançadas e refatoração do projeto original.”
    Cobrança! Reserva tempo para fazer um breve tutorial sobre o assunto.
    Exemplo. A criação de novas entidades no projeto, e como se dá o relacionamento com base no identity. De um para um, para muitos e muitos para muitos. Como se faz e se há uma maneira correta de se fazer.

  11. Ótimo artigo Eduardo .
    sempre acompanho tudo que posta, tudo com excelente qualidade ,
    parabéns .

    Eduardo , uma duvida .

    Caso eu queira mudar os tipos padrões das colunas ( por exemplo , o Id ser um inteiro ) , é possível ?

    • Marcos, acho que o link abaixo resolve seu problema, no caso o autor do vídeo referente alterou o ID para long:

  12. Boa noite

    Estou com dúvidas de como proibir que o usuario efetue login sem antes ativar Pela confirmacao de email. Consegui implementar a confirmacao de email. Contudo se eu tento logar ele llogar normalmente Sem pprecisar.abrir meu email e ativar.

  13. Olá Eduardo!

    Tudo bem? Primeiramente gostaria de parabenizá-lo pelas vídeo aulas. Estão bastante explicativas. Tem me ajudado muito.

    Estou tentando desenvolver uma aplicação baseada naquele seu projeto de arquitetura DDD. Gostaria de saber como faria a integração do Identity com aquele projeto. Como ficaria em relação às camadas. Também vamos usar o NHibernate ao invés do EF.

    Desde já agradeço

    Grande abraço

  14. Eduardo, antes de mais nada obrigado por compartilhar conhecimento.

    Tentei fazer seguindo os passos informados, mas por algum motivo o banco de dados não foi alterado com os novos campos criados para o registro do usuário.

    Quando tento fazer um novo registro mostra a mensagem
    The model backing the ‘ApplicationDbContext’ context has changed since the database was created. Consider using Code First Migrations to update the database.

    Tenho que seguir para fazer o Code First Migrations ou esqueci de algum detalhe para funcionar sem ele ?

  15. Olá Bom dia Eduardo,
    Eu segui o seu tutorial, porém não conseguir obter êxito. Usei o ADD-MIGRATION, UPDATE-DATABASE (meu migration eu não deixei como automático), porém verifiquei que no meu banco não foi adicionado os campos que eu tinha colocado lá na minha RegisterViewModel (registro, perfil, nome).

    Erro: The model backing the ‘ApplicationDbContext’ context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).

    Se puder me ajudar agradeço.

    • Olá boa tarde,
      Eu já consegui. Não quis ficar tentando qual seria a solução. Então eu exclui o Banco e rodei novamente e isto funcionou.

          • Provavelmente, vc deve ter obtido o erro de CodeFirst, basta ir na tabela _MigrationHistory e excluir o registro incluso nela, terá apenas um registro, ele ira rodar e gerar novamente, criando seus novos registros.

  16. Olá Eduardo.
    Existe alguma forma de realizar um relacionamento entre Roles e Claims?
    Ex: a) Para Role “XPTO” existe as Claims “XPTO01” e “XPTO02”.
    b) Para Role “ZWYU” existe as Claims “ZWYU01” e “ZWYU02”

    Abraços.

  17. Olá Eduardo. Parabéns por este material, é muito elucidativo.
    Estou tentando usar o ASP.NET Identity mas aqui na empresa existem muitos sistemas legados que precisamos integrar com os novos sistemas em ASP NET MVC. O meu problema é que tenho que fazer a validação do usuário em uma tabela já existente. Tem como fazer isso? Pois não seria uma customização e sim uma substituição.
    Antecipadamente, agradeço

  18. Bom dia Eduardo.

    Muito bom o tutorial, só não vi no vídeo uma explicação sobre o Checkbox lembrar senha e venho tendo dificuldades para conciliar o lembrar senha com a funcionalidade de deslogar varios clients.

    Obrigado

  19. Olá Eduardo, parabéns pelo posto. Muito bacana. Gostaria de uma ajuda com o Identity por favor. Aqui o cadastro de usuário é por Filial, fiz a customização do ApplicationUser colocando o campo FilialID e o campo na tabela AspNetUsers. Tudo funcionando perfeito. Porém agora na hora do login teria que ser verificado username, senha e filial pois tenho, por exemplo, usuário “eduardo” na filial “01” e usuário “eduardo” na filial “02”. A Filial eu pego por parametro na querystring, e a tela de login continua apenas com os campos UserName e Password. Tem como fazer isso? Por onde eu começo? Desde já agradeço a ajuda. Valeu.

  20. Parabéns Eduardo…
    Gostei muito da forma como é apresentado o Identity!
    Só estou com um problema.
    Como posso fazer para que o usuário do Identity possa esta interagindo com as outras entidades da aplicação, pois são contextos diferente e são mapeados de formas diferente!

    Desde já agradeço, Abraço….

  21. Construi uma aplicação aonde o cadastro das Roles do Usuario seria feita no momento em que cadastro o mesmo, como ficaria essa abordagem?

  22. ESTOU DESENVOLVENDO UM SISTEMA E O ASP.NET IDENTITY CAIU COMO UMA LUVA..NO MEU CASO EU MIGREI O BANCO PARA O SQL…E DEPOIS QUE IMPLEMENTEI AS MODIFICAÇÕES DE REGISTRO VOCÊ TEM QUE ATIVAR O MIGRATIONS E DEPOIS ATUALIZAR VIA CONSOLE.

    GOSTARIA QUE EXPLICASSE COMO GERENCIOS PERMISSOES DE USUARIOS UTILIZANDO AREAS.

    MUITO BOM..VALEW

  23. Parabéns pelo ótimo post Eduardo!
    Me ajudou muito… Obrigado, e mais uma vez, parabéns pelo trabalho!

  24. Como eu faço para exibir o Nome na _LoginPartial?

    @Html.ActionLink(“Olá ” + User.Identity.GetUserName() + …………

    Tentei colocar User.Identity.GetName(), porém não da certo.

    • Como eu faço para o _LoginPartial mostrar o Nome ao invés de UserName?
      Isso mostra o UserName: User.Identity.GetUserName()
      Mas ao invés disso quero que mostre o nome, ex: “João” ao invés de “[email protected]

  25. Eduardo, tudo bem?

    Como eu faria para adicionar uma tabela customizada com informações adicionais do usuário, porém deverá ser preenchida juntamente com o Identity?

    Poderia me ajudar?

    Desde já agradeço.

  26. Como eu faço para o _LoginPartial mostrar o Nome ao invés de UserName?
    Isso mostra o UserName: User.Identity.GetUserName()
    Mas ao invés disso quero que mostre o nome, ex: “João” ao invés de “[email protected]

  27. Eduardo excelente artigo, eu fiquei com uma duvida, caso precise alterar o nome e o sobrenome, como é feito isto?

  28. Olá.. bom dia

    no caso colocar mais campos na classe ApplicationUser só funciona se as tabelas AspRoles…. ainda não existirem… pois no meu caso percebi que aplicar a mudança após já existir alguns usuários cadastrados o sistema gera alguns excepetion… mas se eu deletar as tabelas e iniciar o projeto ele cria a tabela AspUser corretamente conforme apliquei a mudança….

    tentei ativar o Migrations mas ainda sem sucesso.. pois gera mesmos erros

    Poderia me ajudar?

  29. Eduardo excelente artigo, eu fiquei com uma duvida, caso precise alterar o nome e o sobrenome, como é feito isto?

Os comentários estão fechados.