Udostępnij za pośrednictwem


Potwierdzenie konta i odzyskiwanie hasła przy użyciu ASP.NET Identity (C#)

Przed wykonaniem tego samouczka należy najpierw ukończyć Tworzenie bezpiecznej aplikacji internetowej MVC 5 ASP.NET z logowaniem, potwierdzeniem wiadomości e-mail i resetowaniem hasła. Ten samouczek zawiera więcej szczegółów i pokaże, jak skonfigurować pocztę e-mail na potrzeby potwierdzenia konta lokalnego i umożliwić użytkownikom resetowanie zapomnianego hasła w usłudze ASP.NET Identity.

Konto użytkownika lokalnego wymaga, aby użytkownik utworzył hasło dla konta, a hasło jest przechowywane (bezpiecznie) w aplikacji internetowej. ASP.NET Identity obsługuje również konta społecznościowe, które nie wymagają od użytkownika utworzenia hasła dla aplikacji. Konta społecznościowe używają stron trzecich (takich jak Google, Twitter, Facebook lub Microsoft) do uwierzytelniania użytkowników. W tym temacie omówiono następujące zagadnienia:

Nowi użytkownicy rejestrują alias e-mail, który tworzy konto lokalne.

Obraz okna rejestracji konta

Wybranie przycisku Zarejestruj spowoduje wysłanie wiadomości e-mail z potwierdzeniem zawierającej token weryfikacji na ich adres e-mail.

Obraz przedstawiający potwierdzenie wysłania wiadomości e-mail

Użytkownik jest wysyłany e-mail z tokenem potwierdzenia dla swojego konta.

Obraz tokenu potwierdzenia

Wybranie linku potwierdza konto.

Obraz potwierdzający adres e-mail

Odzyskiwanie/resetowanie hasła

Użytkownicy lokalni, którzy zapominają o swoim haśle, mogą mieć token zabezpieczający wysyłany do konta e-mail, umożliwiając im resetowanie hasła.

Obraz przedstawiający okno resetowania hasła dla zapomnianych haseł

Użytkownik wkrótce otrzyma wiadomość e-mail z linkiem umożliwiającym zresetowanie hasła.

Obraz przedstawiający email z resetowaniem hasła
Wybranie linku spowoduje przejście do strony resetowania.

Obraz przedstawiający okno resetowania hasła użytkownika

Wybranie przycisku Resetuj potwierdzi zresetowanie hasła.

Obraz przedstawiający potwierdzenie resetowania hasła

Tworzenie aplikacji internetowej ASP.NET

Rozpocznij od zainstalowania i uruchomienia programu Visual Studio 2017.

  1. Utwórz nowy projekt ASP.NET sieci Web i wybierz szablon MVC. Formularze internetowe obsługują również ASP.NET Identity, dzięki czemu można wykonać podobne kroki w aplikacji formularzy internetowych.

  2. Zmień uwierzytelnianie na indywidualne konta użytkowników.

  3. Uruchom aplikację, wybierz opcję Rejestracja i zarejestruj użytkownika. W tym momencie jedyna weryfikacja adresu e-mail odbywa się za pomocą atrybutu [EmailAddress].

  4. W Eksploratorze serwera przejdź do Połączenia danych\DefaultConnection\Tables\AspNetUsers, kliknij prawym przyciskiem myszy i wybierz Otwórz definicję tabeli.

    Na poniższej ilustracji przedstawiono schemat AspNetUsers:

    obraz przedstawiający schemat użytkowników ASP.NET

  5. Kliknij prawym przyciskiem myszy tabelę AspNetUsers i wybierz Pokaż dane tabeli.

    obraz przedstawiający dane tabeli

    W tym momencie wiadomość e-mail nie została potwierdzona.

Domyślny magazyn danych dla ASP.NET Identity to Entity Framework, ale można go skonfigurować tak, aby korzystał z innych magazynów danych i dodawać dodatkowe pola. Zobacz sekcję Dodatkowe zasoby na końcu tego samouczka.

Klasa uruchamiania OWIN ( Startup.cs ) jest wywoływana podczas uruchamiania aplikacji i wywołuje metodę ConfigureAuth w App_Start\Startup.Auth.cs, która konfiguruje potok OWIN i inicjuje ASP.NET Identity. Sprawdź metodę ConfigureAuth. Każde wywołanie CreatePerOwinContext rejestruje wywołanie zwrotne (zapisane w OwinContext), które będzie wywoływane raz na żądanie w celu utworzenia wystąpienia określonego typu. Możesz ustawić punkt przerwania w konstruktorze i metodzie Create każdego typu (ApplicationDbContext, ApplicationUserManager) i sprawdzić, czy są wywoływane dla każdego żądania. Instancja ApplicationDbContext i ApplicationUserManager jest przechowywana w kontekście OWIN, do którego można uzyskać dostęp w całej aplikacji. ASP.NET Identity integruje się z potokiem OWIN za pośrednictwem średnika plików cookie. Aby uzyskać więcej informacji, zobacz Zarządzanie czasem życia żądania dla klasy UserManager w ASP.NET Identity.

Po zmianie profilu zabezpieczeń zostanie wygenerowana nowa sygnatura zabezpieczeń i zapisana w polu SecurityStamp tabeli AspNetUsers. Należy pamiętać, że pole SecurityStamp różni się od pliku cookie zabezpieczeń. Plik cookie zabezpieczeń nie jest przechowywany w tabeli AspNetUsers (lub gdziekolwiek indziej w usłudze Identity DB). Token cookie zabezpieczeń jest samopodpisany przy użyciu DPAPI i tworzony z informacjami o UserId, SecurityStamp i czasie wygaśnięcia.

Oprogramowanie pośredniczące plików cookie sprawdza plik cookie na każdym żądaniu. Metoda SecurityStampValidator w klasie Startup okresowo odwołuje się do bazy danych i weryfikuje pieczęć zabezpieczeń, zgodnie z validateInterval. Dzieje się tak tylko co 30 minut (w naszym przykładzie), chyba że zmienisz profil zabezpieczeń. Wybrano interwał 30 minut, aby zminimalizować podróże do bazy danych. Aby uzyskać więcej informacji, zobacz samouczek dotyczący uwierzytelniania dwuskładnikowego .

Zgodnie z komentarzami w kodzie metoda UseCookieAuthentication obsługuje uwierzytelnianie plików cookie. Pole SecurityStamp i skojarzony kod zapewniają dodatkową warstwę zabezpieczeń aplikacji, gdy zmienisz hasło, wylogujesz się z zalogowanej przeglądarki. Metoda SecurityStampValidator.OnValidateIdentity umożliwia aplikacji weryfikowanie tokenu zabezpieczającego podczas logowania użytkownika, który jest używany podczas zmiany hasła lub użycia logowania zewnętrznego. Jest to konieczne, aby upewnić się, że wszystkie tokeny (pliki cookie) wygenerowane przy użyciu starego hasła zostaną unieważnione. W przykładowym projekcie, jeśli zmienisz hasło użytkowników, zostanie wygenerowany nowy token dla użytkownika, wszystkie poprzednie tokeny zostaną unieważnione, a pole SecurityStamp zostanie zaktualizowane.

System tożsamości umożliwia skonfigurowanie aplikacji tak, aby po zmianie profilu zabezpieczeń użytkowników (na przykład gdy użytkownik zmieni hasło lub zmienił skojarzony identyfikator logowania (np. z serwisu Facebook, Google, konta Microsoft itp.), użytkownik zostanie wylogowany ze wszystkich wystąpień przeglądarki. Na przykład na poniższej ilustracji przedstawiono przykładową aplikację logowania jednokrotnego , która umożliwia użytkownikowi wylogowanie się ze wszystkich wystąpień przeglądarki (w tym przypadku IE, Firefox i Chrome), wybierając jeden przycisk. Alternatywnie przykład umożliwia wylogowanie się tylko z określonego wystąpienia przeglądarki.

Obraz przedstawiający przykładowe okno aplikacji z wylogowaniem jednokrotnym

Przykładowa aplikacja jednokrotnego wylogowania ilustruje, jak ASP.NET Identity umożliwia ponowne generowanie tokenu zabezpieczającego. Jest to konieczne, aby upewnić się, że wszystkie tokeny (pliki cookie) wygenerowane przy użyciu starego hasła zostaną unieważnione. Ta funkcja zapewnia dodatkową warstwę zabezpieczeń Twojej aplikacji, a po zmianie hasła zostaniesz wylogowany ze wszystkich miejsc, w których byliści zalogowani do tej aplikacji.

Plik App_Start\IdentityConfig.cs zawiera klasy ApplicationUserManager, EmailService i SmsService. Klasy EmailService i SmsService implementują interfejs IIdentityMessageService, dzięki czemu w każdej klasie istnieją typowe metody konfigurowania poczty e-mail i wiadomości SMS. Mimo że w tym samouczku pokazano tylko sposób dodawania powiadomień e-mail za pośrednictwem SendGrid, można wysyłać wiadomości e-mail przy użyciu protokołu SMTP i innych mechanizmów.

Klasa Startup zawiera również płytę kotłową, aby dodać logowania społecznościowe (Facebook, Twitter itp.), zobacz mój samouczek MVC 5 App with Facebook, Twitter, LinkedIn i Google OAuth2 Sign-on, aby uzyskać więcej informacji.

Sprawdź klasę ApplicationUserManager zawierającą informacje o tożsamości użytkowników i konfiguruje następujące funkcje:

  • Wymagania dotyczące siły hasła.
  • Blokada użytkownika (próby i czas).
  • Uwierzytelnianie dwuskładnikowe (2FA). Omówię 2FA i SMS w innym poradniku.
  • Podłączanie usług poczty e-mail i wiadomości SMS. (Omówię wiadomości SMS w innym samouczku).

Klasa ApplicationUserManager dziedziczy z generycznej klasy UserManager<ApplicationUser>. ApplicationUser pochodzi z IdentityUser. IdentityUser pochodzi z ogólnej klasy IdentityUser.

//     Default EntityFramework IUser implementation
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
   where TLogin : IdentityUserLogin<TKey>
   where TRole : IdentityUserRole<TKey>
   where TClaim : IdentityUserClaim<TKey>
{
   public IdentityUser()
   {
      Claims = new List<TClaim>();
      Roles = new List<TRole>();
      Logins = new List<TLogin>();
   }

   ///     User ID (Primary Key)
   public virtual TKey Id { get; set; }

   public virtual string Email { get; set; }
   public virtual bool EmailConfirmed { get; set; }

   public virtual string PasswordHash { get; set; }

   ///     A random value that should change whenever a users credentials have changed (password changed, login removed)
   public virtual string SecurityStamp { get; set; }

   public virtual string PhoneNumber { get; set; }
   public virtual bool PhoneNumberConfirmed { get; set; }

   public virtual bool TwoFactorEnabled { get; set; }

   ///     DateTime in UTC when lockout ends, any time in the past is considered not locked out.
   public virtual DateTime? LockoutEndDateUtc { get; set; }

   public virtual bool LockoutEnabled { get; set; }

   ///     Used to record failures for the purposes of lockout
   public virtual int AccessFailedCount { get; set; }
   
   ///     Navigation property for user roles
   public virtual ICollection<TRole> Roles { get; private set; }

   ///     Navigation property for user claims
   public virtual ICollection<TClaim> Claims { get; private set; }

   ///     Navigation property for user logins
   public virtual ICollection<TLogin> Logins { get; private set; }
   
   public virtual string UserName { get; set; }
}

Powyższe właściwości pokrywają się z właściwościami w tabeli AspNetUsers pokazanej powyżej.

Argumenty ogólne w IUser umożliwiają uzyskiwanie klasy przy użyciu różnych typów dla klucza podstawowego. Zobacz przykład ChangePK, który pokazuje, jak zamienić klucz podstawowy z typu string na int lub GUID.

ApplicationUser

ApplicationUser (public class ApplicationUserManager : UserManager<ApplicationUser>) jest definiowana w Models\IdentityModels.cs jako:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
        UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in 
       //   CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, 
    DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

Wyróżniony kod powyżej generuje ClaimsIdentity. ASP.NET Identity i uwierzytelnianie za pomocą plików cookie OWIN są oparte na żądaniach, dlatego platforma wymaga, aby aplikacja wygenerowała ClaimsIdentity dla użytkownika. ClaimsIdentity zawiera informacje o wszystkich oświadczeniach użytkownika, takich jak nazwa użytkownika, wiek i role, do których należy użytkownik. Możesz również dodać więcej oświadczeń dla użytkownika na tym etapie.

Metoda AuthenticationManager.SignIn OWIN przekazuje ClaimsIdentity i loguje użytkownika:

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties(){
       IsPersistent = isPersistent }, 
       await user.GenerateUserIdentityAsync(UserManager));
}

Aplikacja MVC 5 z usługami logowania Facebook, Twitter, LinkedIn i Google OAuth2 pokazuje, jak można dodać dodatkowe właściwości do klasy ApplicationUser.

Potwierdzenie wiadomości e-mail

Dobrym pomysłem jest potwierdzenie adresu e-mail, z którego rejestruje się nowy użytkownik, aby upewnić się, że nie podszywa się pod kogoś innego (to znaczy, że nie zarejestrował się przy użyciu e-maila innej osoby). Załóżmy, że masz forum dyskusyjne, chcesz uniemożliwić "bob@example.com" zarejestrowanie się jako "joe@contoso.com". Bez potwierdzenia wiadomości e-mail "joe@contoso.com" może dostać niechciane wiadomości e-mail z Twojej aplikacji. Załóżmy, że Bob przypadkowo zarejestrował się jako "bib@example.com" i tego nie zauważył, nie będzie mógł użyć procedury odzyskiwania hasła, ponieważ aplikacja nie ma jego poprawnego adresu e-mail. Potwierdzenie wiadomości e-mail zapewnia tylko ograniczoną ochronę przed botami i nie zapewnia ochrony przed określonymi spamerami. Mają one wiele działających aliasów poczty e-mail, których mogą używać do rejestrowania. W poniższym przykładzie użytkownik nie będzie mógł zmienić swojego hasła, dopóki nie zostanie potwierdzone jego konto (przez wybranie przez nich linku potwierdzenia otrzymanego na koncie e-mail, w ramach którego zarejestrowano). Ten przepływ pracy można zastosować do innych scenariuszy, na przykład wysyłając link w celu potwierdzenia i zresetowania hasła na nowych kontach utworzonych przez administratora, wysyłając użytkownikowi wiadomość e-mail po zmianie profilu itd. Zazwyczaj chcesz uniemożliwić nowym użytkownikom publikowanie wszelkich danych w witrynie internetowej przed potwierdzeniem ich za pomocą poczty e-mail, wiadomości SMS lub innego mechanizmu.

Zbuduj bardziej kompletny przykład

W tej sekcji użyjesz narzędzia NuGet, aby pobrać bardziej kompletny przykład, z którym będziemy pracować.

  1. Utwórz nowy pusty projekt ASP.NET webowy.

  2. W konsoli Menedżera pakietów wprowadź następujące polecenia:

    Install-Package SendGrid
    Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
    

    W tym samouczku użyjemy SendGrid do wysyłania wiadomości e-mail. Pakiet Identity.Samples instaluje kod, z którym będziemy pracować.

  3. Ustaw projekt na używanieSSL.

  4. Przetestuj tworzenie konta lokalnego, uruchamiając aplikację, wybierając link Zarejestruj i publikując formularz rejestracji.

  5. Wybierz link demonstracyjny wiadomości e-mail, który symuluje potwierdzenie wiadomości e-mail.

  6. Usuń kod potwierdzenia linku demonstracyjnego wiadomości e-mail z przykładu (kod ViewBag.Link w kontrolerze konta. Zobacz metody akcji DisplayEmail i ForgotPasswordConfirmation oraz widoki razor ).

Ostrzeżenie

Jeśli zmienisz dowolne ustawienia zabezpieczeń w tym przykładzie, aplikacje produkcyjne będą musiały przejść inspekcję zabezpieczeń, która jawnie wywołuje wprowadzone zmiany.

Przegląd kodu w App_Start\IdentityConfig.cs

W przykładzie pokazano, jak utworzyć konto i dodać je do roli administratora . W przykładzie należy zastąpić wiadomość e-mail pocztą e-mail, której będziesz używać dla konta administratora. Najprostszym sposobem na utworzenie konta administratora jest programowe w metodzie Seed. Mamy nadzieję, że w przyszłości będziemy mieć narzędzie, które umożliwi tworzenie i administrowanie użytkownikami i rolami. Przykładowy kod umożliwia tworzenie użytkowników i ról oraz zarządzanie nimi, ale musisz najpierw mieć konto administratorów, aby uruchamiać role i strony administratora użytkownika. W tym przykładzie konto administratora jest tworzone podczas inicjacji bazy danych.

Zmień hasło i zmień nazwę na konto, na którym można otrzymywać powiadomienia e-mail.

Ostrzeżenie

Zabezpieczenia — nigdy nie przechowuj poufnych danych w kodzie źródłowym.

Jak wspomniano wcześniej, wywołanie app.CreatePerOwinContext w klasie uruchamiania dodaje wywołania zwrotne do metody Create zawartości bazy danych aplikacji, menedżera użytkowników i menedżera ról. Potok OWIN wywołuje metodę Create na tych klasach dla każdego żądania i przechowuje kontekst dla każdej klasy. Kontroler konta uwidacznia menedżera użytkowników z kontekstu HTTP (który zawiera kontekst OWIN):

public ApplicationUserManager UserManager
{
    get
    {
        return _userManager ?? 
    HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
    }
    private set
    {
        _userManager = value;
    }
}

Gdy użytkownik zarejestruje konto lokalne, wywoływana jest metoda HTTP Post Register:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
            var callbackUrl = Url.Action(
               "ConfirmEmail", "Account", 
               new { userId = user.Id, code = code }, 
               protocol: Request.Url.Scheme);

            await UserManager.SendEmailAsync(user.Id, 
               "Confirm your account", 
               "Please confirm your account by clicking this link: <a href=\"" 
                                               + callbackUrl + "\">link</a>");
            // ViewBag.Link = callbackUrl;   // Used only for initial demo.
            return View("DisplayEmail");
        }
        AddErrors(result);
    }

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

Powyższy kod używa danych modelu do utworzenia nowego konta użytkownika przy użyciu wprowadzonego adresu e-mail i hasła. Jeśli alias wiadomości e-mail znajduje się w magazynie danych, tworzenie konta zakończy się niepowodzeniem, a formularz zostanie wyświetlony ponownie. Metoda GenerateEmailConfirmationTokenAsync tworzy bezpieczny token potwierdzenia i przechowuje go w magazynie danych ASP.NET Identity. Metoda Url.Action tworzy link zawierający UserId i token potwierdzenia. Ten link jest następnie wysyłany pocztą e-mail do użytkownika. Użytkownik może wybrać link w swojej aplikacji poczty e-mail, aby potwierdzić swoje konto.

Konfigurowanie potwierdzenia wiadomości e-mail

Przejdź do strony rejestracji usługi SendGrid i zarejestruj się w celu uzyskania bezpłatnego konta. Dodaj kod podobny do poniższego, aby skonfigurować usługę SendGrid:

public class EmailService : IIdentityMessageService
{
   public Task SendAsync(IdentityMessage message)
   {
      return configSendGridasync(message);
   }

   private Task configSendGridasync(IdentityMessage message)
   {
      var myMessage = new SendGridMessage();
      myMessage.AddTo(message.Destination);
      myMessage.From = new System.Net.Mail.MailAddress(
                          "Joe@contoso.com", "Joe S.");
      myMessage.Subject = message.Subject;
      myMessage.Text = message.Body;
      myMessage.Html = message.Body;

      var credentials = new NetworkCredential(
                 ConfigurationManager.AppSettings["mailAccount"],
                 ConfigurationManager.AppSettings["mailPassword"]
                 );

      // Create a Web transport for sending email.
      var transportWeb = new Web(credentials);

      // Send the email.
      if (transportWeb != null)
      {
         return transportWeb.DeliverAsync(myMessage);
      }
      else
      {
         return Task.FromResult(0);
      }
   }
}

Nota

Klienci poczty e-mail często akceptują tylko wiadomości TEKSTOWE (bez kodu HTML). Należy podać komunikat w tekście i kodzie HTML. W powyższym przykładzie usługi SendGrid jest to wykonywane przy użyciu myMessage.Text i myMessage.Html kodu pokazanego powyżej.

Poniższy kod pokazuje, jak wysyłać wiadomości e-mail przy użyciu klasy MailMessage , w której zwraca tylko link.

void sendMail(Message message)
{
#region formatter
   string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
   string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";

   html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion

   MailMessage msg = new MailMessage();
   msg.From = new MailAddress("joe@contoso.com");
   msg.To.Add(new MailAddress(message.Destination));
   msg.Subject = message.Subject;
   msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
   msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));

   SmtpClient smtpClient = new SmtpClient("smtp.gmail.com", Convert.ToInt32(587));
   System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("joe@contoso.com", "XXXXXX");
   smtpClient.Credentials = credentials;
   smtpClient.EnableSsl = true;
   smtpClient.Send(msg);
}

Ostrzeżenie

Zabezpieczenia — nigdy nie przechowuj poufnych danych w kodzie źródłowym. Konto i poświadczenia są przechowywane w appSetting. Na platformie Azure możesz bezpiecznie przechowywać te wartości na karcie Konfigurowanie w portalu Azure. Zobacz Najlepsze rozwiązania dotyczące wdrażania haseł i innych poufnych danych w usługach ASP.NET i Azure.

Wprowadź swoje dane uwierzytelniające SendGrid, uruchom aplikację, zarejestruj się, korzystając z aliasu e-mail, a następnie wybierz link potwierdzający w wiadomości e-mail. Aby dowiedzieć się, jak to zrobić za pomocą konta e-mail Outlook.com, zobacz konfiguracja protokołu SMTP C# Johna Attena dla Outlook.Com hosta SMTP oraz jegoASP.NET Identity 2.0: Konfigurowanie weryfikacji konta i Two-Factor wpisy autoryzacji.

Gdy użytkownik wybierze przycisk Zarejestruj, zostanie wysłana wiadomość e-mail z potwierdzeniem zawierającą token weryfikacji na swój adres e-mail.

Obraz okna potwierdzenia wysłania wiadomości e-mail

Użytkownik jest wysyłany e-mail z tokenem potwierdzenia dla swojego konta.

Obraz odebranej wiadomości e-mail

Przeanalizuj kod

Poniższy kod przedstawia metodę POST ForgotPassword.

public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action("ResetPassword", "Account", 
    new { UserId = user.Id, code = code }, protocol: Request.Url.Scheme);
        await UserManager.SendEmailAsync(user.Id, "Reset Password", 
    "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");        
        return View("ForgotPasswordConfirmation");
    }

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

Metoda kończy się niepowodzeniem w ciszy, jeśli wiadomość e-mail użytkownika nie została potwierdzona. Jeśli dla nieprawidłowego adresu e-mail wystąpił błąd, złośliwi użytkownicy mogą użyć tych informacji, aby znaleźć prawidłowy identyfikator użytkownika (aliasy e-mail), aby przeprowadzić atak.

Poniższy kod przedstawia metodę ConfirmEmail w kontrolerze konta wywoływanym po wybraniu przez użytkownika linku potwierdzenia w wiadomości e-mail wysłanej do nich:

public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    if (userId == null || code == null)
    {
        return View("Error");
    }
    var result = await UserManager.ConfirmEmailAsync(userId, code);
    if (result.Succeeded)
    {
        return View("ConfirmEmail");
    }
    AddErrors(result);
    return View();
}

Po użyciu zapomnianego tokenu hasła zostanie on unieważniony. Następująca zmiana kodu w metodzie Create (w pliku App_Start\IdentityConfig.cs) ustawia tokeny do wygaśnięcia w ciągu 3 godzin.

if (dataProtectionProvider != null)
 {
    manager.UserTokenProvider =
       new DataProtectorTokenProvider<ApplicationUser>
          (dataProtectionProvider.Create("ASP.NET Identity"))
          {                    
             TokenLifespan = TimeSpan.FromHours(3)
          };
 }

Po powyższym kodzie zapomniane hasło i tokeny potwierdzenia wiadomości e-mail wygasną w ciągu 3 godzin. Domyślny TokenLifespan to jeden dzień.

Poniższy kod przedstawia metodę potwierdzenia wiadomości e-mail:

// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
   if (userId == null || code == null)
   {
      return View("Error");
   }
   IdentityResult result;
   try
   {
      result = await UserManager.ConfirmEmailAsync(userId, code);
   }
   catch (InvalidOperationException ioe)
   {
      // ConfirmEmailAsync throws when the userId is not found.
      ViewBag.errorMessage = ioe.Message;
      return View("Error");
   }

   if (result.Succeeded)
   {
      return View();
   }

   // If we got this far, something failed.
   AddErrors(result);
   ViewBag.errorMessage = "ConfirmEmail failed";
   return View("Error");
}

Aby zapewnić bezpieczeństwo aplikacji, usługa ASP.NET Identity obsługuje uwierzytelnianie Two-Factor (2FA). Zobacz ASP.NET Identity 2.0: Weryfikacja konta i konfigurowanie Two-Factor autoryzacji autorstwa Johna Attena. Mimo że można ustawić blokadę konta podczas niepowodzeń prób logowania, takie podejście sprawia, że logowanie jest podatne na blokady systemu DOS. Zalecamy używanie blokady konta tylko w przypadku uwierzytelniania 2FA.

Dodatkowe zasoby