Autenticazione a due fattori tramite SMS e posta elettronica con ASP.NET Identity
di Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi
Questa esercitazione illustra come configurare l'autenticazione a due fattori (2FA) usando SMS e posta elettronica.
Questo articolo è stato scritto da Rick Anderson (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung e Suhas Joshi. L'esempio NuGet è stato scritto principalmente da Hao Kung.
Questo argomento illustra quanto segue:
- Compilazione dell'esempio identity
- Configurare SMS per l'autenticazione a due fattori
- Abilitare l'autenticazione a due fattori
- Come registrare un provider di autenticazione a due fattori
- Combinare account di accesso social e locali
- Blocco dell'account da attacchi di forza bruta
Compilazione dell'esempio identity
In questa sezione si userà NuGet per scaricare un esempio che verrà usato. Iniziare installando ed eseguendo Visual Studio Express 2013 per Web o Visual Studio 2013. Installare Visual Studio 2013 Update 2 o versione successiva.
Nota
Avviso: per completare questa esercitazione è necessario installare Visual Studio 2013 Update 2 .
Creare un nuovo progetto Web di ASP.NET vuoto .
Nella console di Gestione pacchetti immettere i comandi seguenti:
Install-Package SendGrid
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
In questa esercitazione si userà SendGrid per inviare messaggi di posta elettronica e Twilio o ASPSMS per il testo sms. Il
Identity.Samples
pacchetto installa il codice con cui verrà lavorato.Impostare il progetto per l'uso di SSL.
Facoltativo: seguire le istruzioni riportate nell'esercitazione di conferma Email per collegare SendGrid e quindi eseguire l'app e registrare un account di posta elettronica.
Opzionale: Rimuovere il codice di conferma del collegamento di posta elettronica demo dall'esempio (Il
ViewBag.Link
codice nel controller dell'account. Vedere i metodi di azione eForgotPasswordConfirmation
le visualizzazioniDisplayEmail
razor .Opzionale: Rimuovere il
ViewBag.Status
codice dai controller Di gestione e account e dalle visualizzazioni\Account\VerifyCode.cshtml e Views\Manage\VerifyPhoneNumber.cshtml razor. In alternativa, è possibile mantenere la visualizzazione per testare il funzionamento locale diViewBag.Status
questa app senza dover collegare e inviare messaggi di posta elettronica e SMS.
Nota
Avviso: se si modifica una delle impostazioni di sicurezza in questo esempio, le app di produzione dovranno eseguire un controllo di sicurezza che chiama in modo esplicito le modifiche apportate.
Configurare SMS per l'autenticazione a due fattori
Questa esercitazione fornisce istruzioni per l'uso di Twilio o ASPSMS, ma è possibile usare qualsiasi altro provider SMS.
Creazione di un account utente con un provider SMS
Creare un account Twilio o un account ASPSMS .
Installazione di pacchetti aggiuntivi o aggiunta di riferimenti al servizio
Twilio:
Nella Console di Gestione pacchetti immettere il comando seguente:
Install-Package Twilio
ASPSMS:
È necessario aggiungere il riferimento al servizio seguente:Indirizzo:
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Spazio dei nomi:
ASPSMSX2
Comprendere le credenziali utente del provider SMS
Twilio:
Nella scheda Dashboard dell'account Twilio copiare il SID account e il token di autenticazione.ASPSMS:
Dalle impostazioni dell'account passare a Userkey e copiarlo insieme alla password self-defined.In seguito verranno archiviati questi valori nelle variabili
SMSAccountIdentification
eSMSAccountPassword
.Specifica di SenderID/Originator
Twilio:
Nella scheda Numeri copiare il numero di telefono Twilio.ASPSMS:
Nel menu Sblocca origini sbloccare uno o più originatori o scegliere un originatore alfanumerico (non supportato da tutte le reti).In seguito verrà archiviato questo valore nella variabile
SMSAccountFrom
.Trasferimento delle credenziali del provider SMS nell'app
Rendere disponibili le credenziali e il numero di telefono del mittente per l'app:
public static class Keys { public static string SMSAccountIdentification = "My Idenfitication"; public static string SMSAccountPassword = "My Password"; public static string SMSAccountFrom = "+15555551234"; }
Avviso
Sicurezza: non archiviare mai i dati sensibili nel codice sorgente. L'account e le credenziali vengono aggiunti al codice precedente per mantenere l'esempio semplice. Vedere La ASP.NET MVC di Jon Atten: Mantenere le impostazioni private fuori dal controllo del codice sorgente.
Implementazione del trasferimento dei dati al provider SMS
Configurare la
SmsService
classe nel file App_Start\IdentityConfig.cs .A seconda del provider SMS usato, attivare la sezione Twilio o 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 } }
Eseguire l'app e accedere con l'account registrato in precedenza.
Fare clic sull'ID utente, che attiva il
Index
metodo di azione nelManage
controller.Fare clic su Aggiungi.
In pochi secondi si riceverà un messaggio di testo con il codice di verifica. Immetterlo e premere Invia.
La visualizzazione Gestisci mostra che è stato aggiunto il numero di telefono.
Esaminare il codice
// 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);
}
Il Index
metodo di azione nel Manage
controller imposta il messaggio di stato in base all'azione precedente e fornisce collegamenti per modificare la password locale o aggiungere un account locale. Il Index
metodo visualizza anche lo stato o il numero di telefono 2FA, gli account di accesso esterni, 2FA abilitati e ricordano il metodo 2FA per questo browser(spiegato più avanti). Facendo clic sull'ID utente (posta elettronica) nella barra del titolo non viene passato un messaggio. Facendo clic sul numero di telefono : rimuovere il collegamento passa Message=RemovePhoneSuccess
come stringa di query.
https://localhost:44300/Manage?Message=RemovePhoneSuccess
[]
Il AddPhoneNumber
metodo azione visualizza una finestra di dialogo per immettere un numero di telefono che può ricevere messaggi SMS.
// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
Facendo clic sul pulsante Invia codice di verifica viene inviato il numero di telefono al metodo di azione 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 });
}
Il GenerateChangePhoneNumberTokenAsync
metodo genera il token di sicurezza che verrà impostato nel messaggio SMS. Se il servizio SMS è stato configurato, il token viene inviato come stringa "Il codice di sicurezza è <token>". Il SmsService.SendAsync
metodo da chiamare in modo asincrono, quindi l'app viene reindirizzata al VerifyPhoneNumber
metodo action (che visualizza la finestra di dialogo seguente), dove è possibile immettere il codice di verifica.
Dopo aver immesso il codice e fare clic su Invia, il codice viene pubblicato nel metodo di azione 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);
}
Il ChangePhoneNumberAsync
metodo controlla il codice di sicurezza pubblicato. Se il codice è corretto, il numero di telefono viene aggiunto al PhoneNumber
campo della AspNetUsers
tabella. Se la chiamata ha esito positivo, viene chiamato il SignInAsync
metodo:
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));
}
Il isPersistent
parametro imposta se la sessione di autenticazione viene mantenuta in più richieste.
Quando si modifica il profilo di sicurezza, viene generato un nuovo timbro di sicurezza e archiviato nel SecurityStamp
campo della tabella AspNetUsers . Nota, il SecurityStamp
campo è diverso dal cookie di sicurezza. Il cookie di sicurezza non viene archiviato nella AspNetUsers
tabella (o altrove nel database Identity). Il token del cookie di sicurezza è autofirmato usando DPAPI e viene creato con le informazioni sull'ora di UserId, SecurityStamp
scadenza e.
Il middleware del cookie controlla il cookie in ogni richiesta. Il SecurityStampValidator
metodo nella Startup
classe raggiunge il database e controlla periodicamente il timbro di sicurezza, come specificato con .validateInterval
Questo avviene solo ogni 30 minuti (nell'esempio) a meno che non si modifica il profilo di sicurezza. L'intervallo di 30 minuti è stato scelto per ridurre al minimo i viaggi al database.
Il SignInAsync
metodo deve essere chiamato quando viene apportata una modifica al profilo di sicurezza. Quando il profilo di sicurezza viene modificato, il database aggiorna il SecurityStamp
campo e senza chiamare il SignInAsync
metodo in cui si rimane connessi solo fino alla successiva volta che la pipeline OWIN raggiunge il database (l'oggetto validateInterval
). È possibile testare questa operazione modificando il SignInAsync
metodo per restituire immediatamente e impostando la proprietà cookie validateInterval
da 30 minuti a 5 secondi:
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);
Con le modifiche apportate al codice precedente, è possibile modificare il profilo di sicurezza(ad esempio modificando lo stato di Two Factor Enabled) e si verrà disconnessi in 5 secondi quando il SecurityStampValidator.OnValidateIdentity
metodo ha esito negativo. Rimuovere la riga restituita nel SignInAsync
metodo, apportare un'altra modifica del profilo di sicurezza e non verrà disconnessa. Il SignInAsync
metodo genera un nuovo cookie di sicurezza.
Abilitare l'autenticazione a due fattori
Nell'app di esempio è necessario usare l'interfaccia utente per abilitare l'autenticazione a due fattori (2FA). Per abilitare 2FA, fare clic sull'ID utente (alias di posta elettronica) nella barra di spostamento.
Fare clic su abilita 2FA. Disconnettersi, quindi accedere nuovamente. Se il messaggio di posta elettronica è stato abilitato (vedere l'esercitazione precedente), è possibile selezionare l'SMS o il messaggio di posta elettronica per 2FA. La pagina Verifica codice viene visualizzata in cui è possibile immettere il codice (da SMS o posta elettronica). Facendo clic sulla casella di controllo Ricorda questo browser , è necessario usare 2FA per accedere a tale computer e browser. L'abilitazione di 2FA e fare clic su Ricorda questo browser offre una protezione 2FA avanzata dagli utenti malintenzionati che tentano di accedere all'account, purché non abbiano accesso al computer. È possibile eseguire questa operazione in qualsiasi computer privato usato regolarmente. Impostando Ricorda questo browser, si ottiene la sicurezza aggiunta di 2FA dai computer che non si usano regolarmente e si ottiene la praticità su non dover passare attraverso 2FA nei propri computer.
Come registrare un provider di autenticazione a due fattori
Quando si crea un nuovo progetto MVC, il file IdentityConfig.cs contiene il codice seguente per registrare un provider di autenticazione a due fattori:
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;
}
Aggiungere un numero di telefono per 2FA
Il AddPhoneNumber
metodo di azione nel Manage
controller genera un token di sicurezza e lo invia al numero di telefono specificato.
[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 });
}
Dopo aver inviato il token, reindirizza al VerifyPhoneNumber
metodo azione, dove è possibile immettere il codice per registrare SMS per 2FA. SMS 2FA non viene usato finché non è stato verificato il numero di telefono.
Abilitazione di 2FA
Il EnableTFA
metodo action abilita 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");
}
Nota che SignInAsync
deve essere chiamato perché enable 2FA è una modifica al profilo di sicurezza. Quando la funzionalità 2FA è abilitata, l'utente dovrà usare 2FA per accedere, usando gli approcci 2FA registrati (SMS e posta elettronica nell'esempio).
È possibile aggiungere altri provider 2FA, ad esempio generatori di codice a matrice o è possibile scrivere il proprio.
Nota
I codici 2FA vengono generati usando algoritmo password one-time basato su tempo e i codici sono validi per sei minuti. Se si richiedono più di sei minuti per immettere il codice, verrà visualizzato un messaggio di errore di codice non valido.
Combinare account di accesso social e locali
È possibile combinare account locali e social facendo clic sul collegamento di posta elettronica. Nella sequenza seguente vieneRickAndMSFT@gmail.com prima creato come account di accesso locale, ma è possibile creare l'account come account di social log in primo luogo, quindi aggiungere un account di accesso locale.
Fare clic sul collegamento Gestisci . Si notino i 0 account esterni (account di accesso social) associati a questo account.
Fare clic sul collegamento a un altro servizio di accesso e accettare le richieste dell'app. I due account sono stati combinati, sarà possibile accedere con entrambi gli account. È possibile che gli utenti vogliano aggiungere account locali nel caso in cui il servizio di autenticazione dei social log sia inattivo o più probabilmente hanno perso l'accesso al proprio account social.
Nell'immagine seguente Tom è un accesso social (che è possibile visualizzare dagli account di accesso esterni: 1 visualizzato nella pagina).
Facendo clic su Seleziona una password è possibile aggiungere un accesso locale associato allo stesso account.
Blocco dell'account da attacchi di forza bruta
È possibile proteggere gli account nell'app da attacchi di dizionario abilitando il blocco utente. Il codice seguente nel metodo configura il ApplicationUserManager Create
blocco:
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
Il codice precedente abilita il blocco solo per l'autenticazione a due fattori. Anche se è possibile abilitare il blocco per gli account di accesso modificando shouldLockout
in true nel Login
metodo del controller di account, è consigliabile non abilitare il blocco per gli account di accesso perché rende l'account soggetto agli attacchi di accesso DOS . Nel codice di esempio il blocco è disabilitato per l'account amministratore creato nel ApplicationDbInitializer Seed
metodo:
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);
}
}
Richiedere a un utente di avere un account di posta elettronica convalidato
Il codice seguente richiede a un utente di avere un account di posta elettronica convalidato prima di poter accedere:
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);
}
}
Come SignInManager controlla il requisito 2FA
Sia l'accesso locale che l'accesso social per verificare se 2FA è abilitato. Se 2FA è abilitato, il SignInManager
metodo di accesso restituisce SignInStatus.RequiresVerification
e l'utente verrà reindirizzato al SendCode
metodo di azione, dove dovranno immettere il codice per completare la sequenza di accesso. Se l'utente ha RememberMe è impostato sul cookie locale degli utenti, il SignInManager
restituirà SignInStatus.Success
e non dovrà passare attraverso 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 });
}
}
Il codice seguente mostra il SendCode
metodo action. Viene creato un oggetto SelectListItem con tutti i metodi 2FA abilitati per l'utente. SelectListItem viene passato all'helper DropDownListFor, che consente all'utente di selezionare l'approccio 2FA (in genere e-mail e 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 });
}
Dopo che l'utente pubblica l'approccio 2FA, viene chiamato il HTTP POST SendCode
metodo action, SignInManager
invia il codice 2FA e l'utente viene reindirizzato al VerifyCode
metodo azione in cui possono immettere il codice per completare l'accesso.
//
// 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 });
}
Blocco 2FA
Anche se è possibile impostare il blocco dell'account in caso di errori di tentativi di password di accesso, questo approccio rende l'accesso soggetto ai blocchi DOS . È consigliabile usare il blocco dell'account solo con 2FA. ApplicationUserManager
Quando viene creato, il codice di esempio imposta il blocco 2FA e MaxFailedAccessAttemptsBeforeLockout
su cinque. Dopo che un utente accede (tramite account locale o account social), ogni tentativo non riuscito a 2FA viene archiviato e, se il massimo tentativo viene raggiunto, l'utente viene bloccato per cinque minuti (è possibile impostare il tempo di blocco con DefaultAccountLockoutTimeSpan
).
Risorse aggiuntive
- ASP.NET risorse consigliate per l'identità Elenco completo di blog di identità, video, esercitazioni e collegamenti SO eccezionali.
- MVC 5 App con Facebook, Twitter, LinkedIn e Google OAuth2 Sign-On illustra anche come aggiungere informazioni sul profilo alla tabella utenti.
- ASP.NET MVC e Identity 2.0: Informazioni sulle nozioni di base di John Atten.
- Conferma dell'account e ripristino password con identità ASP.NET
- Introduzione ad ASP.NET Identity
- Annuncio di RTM di ASP.NET Identity 2.0.0 da Pranav Rastogi.
- ASP.NET identità 2.0: Configurazione della convalida dell'account e autorizzazione Two-Factor di John Atten.