Sdílet prostřednictvím


Dvoufaktorové ověřování pomocí SMS a e-mailu s ASP.NET Identity

Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi

V tomto kurzu se dozvíte, jak nastavit dvojúrovňové ověřování (2FA) pomocí SMS a e-mailu.

Tento článek napsali Rick Anderson (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung a Suhas Joshi. Ukázku NuGet napsal primárně Hao Kung.

Toto téma se zabývá následujícími tématy:

Vytvoření ukázky identity

V této části použijete NuGet ke stažení ukázky, se kterou budeme pracovat. Začněte instalací a spuštěním Visual Studio Express 2013 pro web nebo Visual Studio 2013. Nainstalujte visual Studio 2013 Update 2 nebo novější.

Poznámka

Upozornění: K dokončení tohoto kurzu musíte nainstalovat Visual Studio 2013 Update 2 .

  1. Vytvořte nový prázdný ASP.NET webový projekt.

  2. V konzole Správce balíčků zadejte následující příkazy:

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

    V tomto kurzu použijeme SendGrid k odesílání e-mailů a Twilio nebo ASPSMS pro sms zprávy. Balíček Identity.Samples nainstaluje kód, se kterým budeme pracovat.

  3. Nastavte projekt tak, aby používal SSL.

  4. Volitelné: Postupujte podle pokynů v kurzu potvrzení Email a připojte SendGrid, spusťte aplikaci a zaregistrujte e-mailový účet.

  5. Volitelné: Odeberte z ukázky potvrzovací kód odkazu na ukázkový e-mail (Kód ViewBag.Link v kontroleru účtu. DisplayEmail Viz metody akcí a ForgotPasswordConfirmation a zobrazení razor ).

  6. Volitelné:ViewBag.Status Odeberte kód z řadičů Manage a Account a ze zobrazení Razor Views\Account\VerifyCode.cshtml a Views\Manage\VerifyPhoneNumber.cshtml . Případně si můžete nechat ViewBag.Status displej a otestovat, jak tato aplikace funguje místně, aniž byste se museli připojovat a posílat e-maily a SMS zprávy.

Poznámka

Upozornění: Pokud změníte některé z nastavení zabezpečení v této ukázce, produkční aplikace budou muset projít auditem zabezpečení, který explicitně vyvolá provedené změny.

Nastavení sms pro dvojúrovňové ověřování

Tento kurz obsahuje pokyny pro použití Twilia nebo ASPSMS, ale můžete použít libovolného jiného poskytovatele serveru SMS.

  1. Vytvoření uživatelského účtu u poskytovatele serveru SMS

    Vytvořte účet Twilio nebo ASPSMS .

  2. Instalace dalších balíčků nebo přidání odkazů na služby

    Twilio:
    V konzole Správce balíčků zadejte následující příkaz:
    Install-Package Twilio

    ASPSMS:
    Je potřeba přidat následující odkaz na službu:

    Obrázek okna s odkazem na přidání služby

    Address:
    https://webservice.aspsms.com/aspsmsx2.asmx?WSDL

    Obor názvů:
    ASPSMSX2

  3. Zjištění přihlašovacích údajů uživatele poskytovatele SERVERU SMS

    Twilio:
    Na kartě Řídicí panel účtu Twilio zkopírujte IDENTIFIKÁTOR SID účtu a token ověřování.

    ASPSMS:
    V nastavení účtu přejděte na Userkey (Klíč uživatele ) a zkopírujte ho společně s vlastním definovaným heslem.

    Později tyto hodnoty uložíme do proměnných SMSAccountIdentification a SMSAccountPassword .

  4. Určení SenderID / Původce

    Twilio:
    Na kartě Čísla zkopírujte svoje telefonní číslo Twilio.

    ASPSMS:
    V nabídce Odemknout původce odemkněte jednoho nebo více původců nebo zvolte alfanumerický původce (nepodporuje ho všechny sítě).

    Později tuto hodnotu uložíme do proměnné SMSAccountFrom .

  5. Přenos přihlašovacích údajů poskytovatele SMS do aplikace

    Zpřístupněte aplikaci přihlašovací údaje a telefonní číslo odesílatele:

    public static class Keys
    {
       public static string SMSAccountIdentification = "My Idenfitication";
       public static string SMSAccountPassword = "My Password";
       public static string SMSAccountFrom = "+15555551234";
    }
    

    Upozornění

    Zabezpečení – nikdy neukládejte citlivá data ve zdrojovém kódu. Účet a přihlašovací údaje se přidají do výše uvedeného kódu, aby byla ukázka jednoduchá. Viz ASP.NET MVC od Jona Attena : Zachování privátních nastavení mimo správu zdrojového kódu.

  6. Implementace přenosu dat k poskytovateli serveru SMS

    SmsService Nakonfigurujte třídu v souboru App_Start\IdentityConfig.cs.

    V závislosti na použitém poskytovateli serveru SMS aktivujte část Twilio nebo ASPSMS :

    public class SmsService : IIdentityMessageService
    {
        public Task SendAsync(IdentityMessage message)
        {
            // Twilio Begin
            // var Twilio = new TwilioRestClient(
            //   Keys.SMSAccountIdentification,
            //   Keys.SMSAccountPassword);
            // var result = Twilio.SendMessage(
            //   Keys.SMSAccountFrom,
            //   message.Destination, message.Body
            // );
            // Status is one of Queued, Sending, Sent, Failed or null if the number is not valid
            // Trace.TraceInformation(result.Status);
            // Twilio doesn't currently have an async API, so return success.
            // return Task.FromResult(0);
            // Twilio End
    
            // ASPSMS Begin 
            // var soapSms = new WebApplication1.ASPSMSX2.ASPSMSX2SoapClient("ASPSMSX2Soap");
            // soapSms.SendSimpleTextSMS(
            //   Keys.SMSAccountIdentification,
            //   Keys.SMSAccountPassword,
            //   message.Destination,
            //   Keys.SMSAccountFrom,
            //   message.Body);
            // soapSms.Close();
            // return Task.FromResult(0);
            // ASPSMS End
        }
    }
    
  7. Spusťte aplikaci a přihlaste se pomocí účtu, který jste předtím zaregistrovali.

  8. Klikněte na id uživatele, čímž se aktivuje Index metoda akce v Manage kontroleru.

    Obrázek zaregistrovaného účtu přihlášeného k aplikaci

  9. Klikněte na Přidat.

    Obrázek odkazu přidat telefonní číslo

  10. Za několik sekund dostanete textovou zprávu s ověřovacím kódem. Zadejte ho a stiskněte Odeslat.

    Obrázek znázorňující zadání ověřovacího kódu telefonu

  11. Zobrazení Spravovat zobrazuje, že vaše telefonní číslo bylo přidáno.

    Obrázek okna spravovat zobrazení s telefonním číslem

Kontrola kódu

// GET: /Account/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
    ViewBag.StatusMessage =
        message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
        : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
        : message == ManageMessageId.SetTwoFactorSuccess ? "Your two factor provider has been set."
        : message == ManageMessageId.Error ? "An error has occurred."
        : message == ManageMessageId.AddPhoneSuccess ? "The phone number was added."
        : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
        : "";

    var model = new IndexViewModel
    {
        HasPassword = HasPassword(),
        PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
        TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
        Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
        BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId())
    };
    return View(model);
}

Metoda Index akce v Manage kontroleru nastaví stavovou zprávu na základě předchozí akce a poskytuje odkazy na změnu místního hesla nebo přidání místního účtu. Metoda Index také zobrazí stav nebo vaše telefonní číslo 2FA, externí přihlášení, povolené 2FA a zapamatuje si metodu 2FA pro tento prohlížeč (vysvětlení dále). Kliknutím na ID uživatele (e-mail) v záhlaví se zpráva nepředá. Kliknutím na telefonní číslo: odebrat odkaz se předá Message=RemovePhoneSuccess jako řetězec dotazu.

https://localhost:44300/Manage?Message=RemovePhoneSuccess

[Obrázek odebraného telefonního čísla]

Metoda AddPhoneNumber akce zobrazí dialogové okno pro zadání telefonního čísla, které může přijímat zprávy SMS.

// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
   return View();
}

Obrázek dialogového okna s akcí přidat telefonní číslo

Kliknutím na tlačítko Odeslat ověřovací kód odešlete telefonní číslo do metody akce HTTP POST AddPhoneNumber .

// POST: /Account/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    // Generate the token 
    var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
                               User.Identity.GetUserId(), model.Number);
    if (UserManager.SmsService != null)
    {
        var message = new IdentityMessage
        {
            Destination = model.Number,
            Body = "Your security code is: " + code
        };
        // Send token
        await UserManager.SmsService.SendAsync(message);
    }
    return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}

Metoda GenerateChangePhoneNumberTokenAsync vygeneruje token zabezpečení, který se nastaví ve zprávě SMS. Pokud je služba SMS nakonfigurovaná, token se odešle jako řetězec "Váš bezpečnostní kód je <token>". Metoda SmsService.SendAsync pro se volá asynchronně, aplikace se pak přesměruje na metodu VerifyPhoneNumber akce (která zobrazí následující dialogové okno), kde můžete zadat ověřovací kód.

Obrázek dialogového okna metody akce ověření telefonního čísla

Jakmile kód zadáte a kliknete na odeslat, odešle se do metody akce HTTP POST VerifyPhoneNumber .

// POST: /Account/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
    if (result.Succeeded)
    {
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
    }
    // If we got this far, something failed, redisplay form
    ModelState.AddModelError("", "Failed to verify phone");
    return View(model);
}

Metoda ChangePhoneNumberAsync zkontroluje zaúčtované bezpečnostní kódy. Pokud je kód správný, telefonní číslo se přidá do PhoneNumber pole AspNetUsers tabulky. Pokud je toto volání úspěšné, volá se SignInAsync metoda:

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
   // Clear the temporary cookies used for external and two factor sign ins
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, 
       DefaultAuthenticationTypes.TwoFactorCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties
    {
       IsPersistent = isPersistent 
    }, 
       await user.GenerateUserIdentityAsync(UserManager));
}

Parametr isPersistent nastaví, jestli je relace ověřování trvalá ve více požadavcích.

Při změně profilu zabezpečení se vygeneruje nové razítko zabezpečení a uloží se do SecurityStamp pole tabulky AspNetUsers . Všimněte si, že pole SecurityStamp se liší od souboru cookie zabezpečení. Soubor cookie zabezpečení se neukládá do AspNetUsers tabulky (ani nikde jinde v databázi identit). Token souboru cookie zabezpečení je podepsaný svým držitelem pomocí rozhraní DPAPI a vytvoří se s informacemi UserId, SecurityStamp o době vypršení platnosti a .

Middleware souborů cookie kontroluje soubor cookie u každého požadavku. Metoda SecurityStampValidator ve Startup třídě narazí na databázi a pravidelně kontroluje razítko zabezpečení, jak je uvedeno v parametru validateInterval. K tomu dochází pouze každých 30 minut (v naší ukázce), pokud nezměníte svůj bezpečnostní profil. 30minutový interval byl zvolen, aby se minimalizovaly cesty do databáze.

Metodu SignInAsync je potřeba volat při jakékoli změně profilu zabezpečení. Když se změní profil zabezpečení, databáze aktualizuje SecurityStamp pole a bez volání SignInAsync metody byste zůstali přihlášeni pouze do chvíle, kdy kanál OWIN narazí na databázi ().validateInterval Můžete to otestovat tak, že změníte metodu SignInAsync tak, aby se vracela okamžitě, a nastavením vlastnosti cookie validateInterval z 30 minut na 5 sekund:

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
   return;

   // Clear any partial cookies from external or two factor partial sign ins
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, 
       DefaultAuthenticationTypes.TwoFactorCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties
    {
       IsPersistent = isPersistent 
    }, 
       await user.GenerateUserIdentityAsync(UserManager));
}
public void ConfigureAuth(IAppBuilder app) {
    // Configure the db context, user manager and role manager to use a single instance per request
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

    // Enable the application to use a cookie to store information for the signed in user
    // and to use a cookie to temporarily store information about a user logging in with a 
    // third party login provider
    // Configure the sign in cookie
    app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider {
            // Enables the application to validate the security stamp when the user logs in.
            // This is a security feature which is used when you change a password or add 
            // an external login to your account.  
            OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                //validateInterval: TimeSpan.FromMinutes(30),
                validateInterval: TimeSpan.FromSeconds(5),
                regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
        }
    });
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

Pomocí výše uvedených změn kódu můžete změnit svůj profil zabezpečení (například změnou stavu Dvoufaktorové povolené) a budete odhlášeni za 5 sekund, když SecurityStampValidator.OnValidateIdentity metoda selže. Odeberte návratový řádek v SignInAsync metodě, proveďte další změnu profilu zabezpečení a nebudete odhlášeni. Metoda SignInAsync vygeneruje nový soubor cookie zabezpečení.

Povolení dvoufaktorového ověřování

V ukázkové aplikaci musíte k povolení dvoufaktorového ověřování (2FA) použít uživatelské rozhraní. Pokud chcete povolit 2FA, klikněte na id uživatele (e-mailový alias) na navigačním panelu. Obrázek U I pro povolení dvojúrovňového ověřování
Klikněte na povolit 2FA. Obrázek po kliknutí na I D uživatele se zobrazeným odkazem na povolení dvojúrovňového ověřování Odhlaste se a pak se znovu přihlaste. Pokud jste povolili e-mail (viz můj předchozí kurz), můžete vybrat SMS nebo e-mail pro 2FA. Obrázek zobrazující možnosti odeslání ověření Zobrazí se stránka Ověřit kód, na které můžete kód zadat (z SMS nebo e-mailu). Obrázek znakové stránky ověření Když kliknete na zaškrtávací políčko Pamatovat si tento prohlížeč , nebudete muset k přihlášení k tomuto počítači a prohlížeči používat 2FA. Když povolíte 2FA a kliknete na pamatovat si tento prohlížeč , získáte silnou ochranu 2FA před uživateli se zlými úmysly, kteří se pokoušejí získat přístup k vašemu účtu, pokud nemají přístup k vašemu počítači. Můžete to udělat na jakémkoli privátním počítači, který pravidelně používáte. Nastavením pamatovat si tento prohlížeč získáte z počítačů, které pravidelně nepoužíváte, zvýšené zabezpečení 2FA a získáte pohodlí, když nemusíte procházet 2FA na vlastních počítačích.

Postup registrace zprostředkovatele dvojúrovňového ověřování

Když vytvoříte nový projekt MVC, soubor IdentityConfig.cs obsahuje následující kód pro registraci zprostředkovatele dvojúrovňového ověřování:

public static ApplicationUserManager Create(
   IdentityFactoryOptions<ApplicationUserManager> options, 
   IOwinContext context) 
{
    var manager = new ApplicationUserManager(
       new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
    // Configure validation logic for usernames
    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
    {
        AllowOnlyAlphanumericUserNames = false,
        RequireUniqueEmail = true
    };
    // Configure validation logic for passwords
    manager.PasswordValidator = new PasswordValidator
    {
        RequiredLength = 6,
        RequireNonLetterOrDigit = true,
        RequireDigit = true,
        RequireLowercase = true,
        RequireUppercase = true,
    };
    // Register two factor authentication providers. This application uses Phone and Emails as a 
    // step of receiving a code for verifying the user
    // You can write your own provider and plug it in here.
    manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
    {
        MessageFormat = "Your security code is: {0}"
    });
    manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
    {
        Subject = "Security Code",
        BodyFormat = "Your security code is: {0}"
    });
    manager.EmailService = new EmailService();
    manager.SmsService = new SmsService();

    var dataProtectionProvider = options.DataProtectionProvider;
    if (dataProtectionProvider != null)
    {
        manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>
           (dataProtectionProvider.Create("ASP.NET Identity"));
    }
    return manager;
}

Přidání telefonního čísla pro 2FA

Metoda AddPhoneNumber akce v Manage kontroleru vygeneruje token zabezpečení a odešle ho na telefonní číslo, které jste zadali.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    // Generate the token and send it
    var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
       User.Identity.GetUserId(), model.Number);
    if (UserManager.SmsService != null)
    {
        var message = new IdentityMessage
        {
            Destination = model.Number,
            Body = "Your security code is: " + code
        };
        await UserManager.SmsService.SendAsync(message);
    }
    return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}

Po odeslání tokenu se přesměruje na metodu VerifyPhoneNumber akce, kde můžete zadat kód pro registraci SMS pro 2FA. SMS 2FA se nepoužívá, dokud neověříte telefonní číslo.

Povolení 2FA

Metoda EnableTFA action povolí 2FA:

// POST: /Manage/EnableTFA
[HttpPost]
public async Task<ActionResult> EnableTFA()
{
    await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
    if (user != null)
    {
        await SignInAsync(user, isPersistent: false);
    }
    return RedirectToAction("Index", "Manage");
}

Všimněte si, SignInAsync že je nutné volat, protože povolení 2FA je změna profilu zabezpečení. Když je 2FA povolená, bude uživatel muset k přihlášení použít 2FA s použitím 2FA přístupů, které zaregistroval (SMS a e-mail v ukázce).

Můžete přidat další poskytovatele 2FA, jako jsou generátory kódů QR, nebo můžete napsat vlastní.

Poznámka

Kódy 2FA jsou generovány pomocí algoritmu jednorázového hesla založeného na čase a kódy jsou platné po dobu šesti minut. Pokud zadání kódu trvá déle než šest minut, zobrazí se chybová zpráva Neplatný kód.

Kombinování sociálních a místních přihlašovacích účtů

Místní účty a účty na sociálních sítích můžete zkombinovat kliknutím na e-mailový odkaz. V následujícím pořadíRickAndMSFT@gmail.com se nejprve vytvoří jako místní přihlášení, ale nejprve můžete vytvořit účet jako přihlášení na sociální sítě a pak přidat místní přihlášení.

Obrázek výběru odkazu na e-mail

Klikněte na odkaz Spravovat . Všimněte si 0 externích přihlašovacích údajů (přihlašovacích údajů sociálních sítí) přidružených k tomuto účtu.

Obrázek zobrazující další stránku a výběr možnosti Spravovat

Klikněte na odkaz na jinou přihlašovací službu a přijměte žádosti aplikace. Oba účty byly sloučeny, budete se moct přihlásit pomocí jednoho z těchto účtů. Můžete chtít, aby vaši uživatelé přidali místní účty v případě, že je jejich služba pro ověřování sociálních sítí mimo provoz nebo je pravděpodobnější, že ztratili přístup ke svému účtu na sociální sítě.

Na následujícím obrázku je Tom sociální přihlášení (které můžete vidět v externích přihlášeních: 1 na stránce).

Obrázek znázorňující externí přihlášení a umístění výběru hesla

Kliknutím na Vybrat heslo můžete přidat místní přihlášení přidružené ke stejnému účtu.

Obrázek stránky s výběrem hesla

Uzamčení účtu před útoky hrubou silou

Účty ve vaší aplikaci můžete chránit před slovníkovými útoky povolením uzamčení uživatele. Následující kód v ApplicationUserManager Create metodě konfiguruje uzamčení:

// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;

Výše uvedený kód umožňuje uzamčení pouze pro dvoufaktorové ověřování. I když můžete povolit uzamčení přihlášení změnou shouldLockout na true v Login metodě kontroleru účtů, doporučujeme nepovolit uzamčení přihlášení, protože účet je náchylný k útokům na přihlášení doS . V ukázkovém kódu je uzamčení pro účet správce vytvořený v ApplicationDbInitializer Seed metodě zakázané:

public static void InitializeIdentityForEF(ApplicationDbContext db)
{
    var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
    var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
    const string name = "admin@example.com";
    const string roleName = "Admin";

    //Create Role Admin if it does not exist
    var role = roleManager.FindByName(roleName);
    if (role == null)
    {
        role = new IdentityRole(roleName);
        var roleresult = roleManager.Create(role);
    }

    var user = userManager.FindByName(name);
    if (user == null)
    {
        user = new ApplicationUser { UserName = name, Email = name };
        var result = userManager.Create(user, GetSecurePassword());
        result = userManager.SetLockoutEnabled(user.Id, false);
    }

    // Add user admin to Role Admin if not already added
    var rolesForUser = userManager.GetRoles(user.Id);
    if (!rolesForUser.Contains(role.Name))
    {
        var result = userManager.AddToRole(user.Id, role.Name);
    }
}

Vyžadování, aby uživatel měl ověřený e-mailový účet

Následující kód vyžaduje, aby měl uživatel před přihlášením ověřený e-mailový účet:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

   // Require the user to have a confirmed email before they can log on.
    var user = await UserManager.FindByNameAsync(model.Email);
    if (user != null)
    {
       if (!await UserManager.IsEmailConfirmedAsync(user.Id))
       {
          ViewBag.errorMessage = "You must have a confirmed email to log on.";
          return View("Error");
       }         
    }
    // This doen't count login failures towards lockout only two factor authentication
    // To enable password failures to trigger lockout, change to shouldLockout: true
    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, 
       model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}

Jak SignInManager kontroluje požadavek na 2FA

Místní přihlášení i přihlášení na sociální sítě zkontroluje, jestli je povolené 2FA. Pokud je povoleno 2FA, SignInManager vrátí SignInStatus.RequiresVerificationmetoda přihlášení a uživatel bude přesměrován na metodu SendCode akce, kde bude muset zadat kód pro dokončení protokolu v pořadí. Pokud má uživatel Nastaveno RememberMe na místním souboru cookie uživatelů, SignInManager vrátí se SignInStatus.Success a nebude muset projít 2FA.

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

   // Require the user to have a confirmed email before they can log on.
    var user = await UserManager.FindByNameAsync(model.Email);
    if (user != null)
    {
       if (!await UserManager.IsEmailConfirmedAsync(user.Id))
       {
          ViewBag.errorMessage = "You must have a confirmed email to log on.";
          return View("Error");
       }         
    }
    // This doen't count login failures towards lockout only two factor authentication
    // To enable password failures to trigger lockout, change to shouldLockout: true
    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, 
       model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return RedirectToAction("Login");
    }

    // Sign in the user with this external login provider if the user already has a login
    var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
        case SignInStatus.Failure:
        default:
            // If the user does not have an account, then prompt the user to create an account
            ViewBag.ReturnUrl = returnUrl;
            ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
            return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
    }
}

Následující kód ukazuje metodu SendCode akce. Vytvoří se SelectListItem se všemi povolenými metodami 2FA pro uživatele. Rutina SelectListItem se předá pomocné rutině DropDownListFor, která uživateli umožňuje vybrat přístup 2FA (obvykle e-mail a SMS).

public async Task<ActionResult> SendCode(string returnUrl)
{
    var userId = await SignInManager.GetVerifiedUserIdAsync();
    if (userId == null)
    {
        return View("Error");
    }
    var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
    var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
    return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl });
}

Jakmile uživatel publikuje přístup 2FA, HTTP POST SendCode volá se metoda akce, SignInManager odešle kód 2FA a uživatel je přesměrován na metodu VerifyCode akce, kde může zadat kód pro dokončení přihlášení.

//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SendCode(SendCodeViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View();
    }

    // Generate the token and send it
    if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
    {
        return View("Error");
    }
    return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl });
}

Uzamčení 2FA

I když můžete nastavit uzamčení účtu při neúspěšných pokusech o přihlášení pomocí hesla, díky tomuto přístupu bude vaše přihlášení náchylné k uzamčení SYSTÉMU DOS . Doporučujeme používat uzamčení účtu jenom s 2FA. ApplicationUserManager Po vytvoření nastaví ukázkový kód uzamčení 2FA a MaxFailedAccessAttemptsBeforeLockout na pět. Jakmile se uživatel přihlásí (prostřednictvím místního účtu nebo účtu na sociálních sítích), uloží se každý neúspěšný pokus o 2FA. Pokud je dosaženo maximálního počtu pokusů, uživatel se na pět minut uzamkne (můžete nastavit dobu uzamčení pomocí DefaultAccountLockoutTimeSpanpříkazu ).

Další materiály