Authentification à deux facteurs par SMS et e-mail avec ASP.NET Identity
par Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi
Ce tutoriel vous montre comment configurer l’authentification à deux facteurs (2FA) à l’aide d’un SMS et d’un e-mail.
Cet article a été écrit par Rick Anderson (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung et Suhas Joshi. L’exemple NuGet a été écrit principalement par Hao Kung.
Cette rubrique couvre les sujets suivants :
- Génération de l’exemple d’identité
- Configurer SMS pour l’authentification à deux facteurs
- Activer l’authentification à deux facteurs
- Comment inscrire un fournisseur d’authentification à deux facteurs
- Combiner des comptes de connexion sociaux et locaux
- Verrouillage de compte à partir d’attaques par force brute
Génération de l’exemple d’identité
Dans cette section, vous allez utiliser NuGet pour télécharger un exemple avec lequel nous allons travailler. Commencez par installer et exécuter Visual Studio Express 2013 pour le web ou Visual Studio 2013. Installez Visual Studio 2013 Update 2 ou version ultérieure.
Notes
Avertissement : Vous devez installer Visual Studio 2013 Update 2 pour suivre ce didacticiel.
Créez un projet web ASP.NET vide .
Dans la console du Gestionnaire de package, entrez les commandes suivantes :
Install-Package SendGrid
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
Dans ce tutoriel, nous allons utiliser SendGrid pour envoyer des messages électroniques et Twilio ou ASPSMS pour les SMS. Le
Identity.Samples
package installe le code avec lequel nous allons travailler.Définissez le projet pour utiliser SSL.
Facultatif : suivez les instructions de mon didacticiel de confirmation Email pour raccorder SendGrid, puis exécutez l’application et inscrivez un compte de messagerie.
Optionnel: Supprimez le code de confirmation du lien d’e-mail de démonstration de l’exemple (code
ViewBag.Link
dans le contrôleur de compte. Consultez les méthodes d’actionDisplayEmail
et etForgotPasswordConfirmation
les vues razor ).Optionnel: Supprimez le
ViewBag.Status
code des contrôleurs Gérer et Compte et des vues razor Views\Account\VerifyCode.cshtml et Views\Manage\VerifyPhoneNumber.cshtml . Vous pouvez également conserver l’affichage pour tester leViewBag.Status
fonctionnement de cette application localement sans avoir à vous connecter et à envoyer des e-mails et des SMS.
Notes
Avertissement : si vous modifiez l’un des paramètres de sécurité de cet exemple, les applications de production doivent subir un audit de sécurité qui appelle explicitement les modifications apportées.
Configurer SMS pour l’authentification à deux facteurs
Ce tutoriel fournit des instructions pour utiliser Twilio ou ASPSMS, mais vous pouvez utiliser n’importe quel autre fournisseur SMS.
Création d’un compte d’utilisateur avec un fournisseur SMS
Installation de packages supplémentaires ou ajout de références de service
Twilio:
Dans la Console du gestionnaire de package, entrez la commande suivante :
Install-Package Twilio
ASPSMS :
La référence de service suivante doit être ajoutée :Adresse :
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Espace de noms :
ASPSMSX2
Identification des informations d’identification de l’utilisateur du fournisseur SMS
Twilio:
Sous l’onglet Tableau de bord de votre compte Twilio, copiez le SID de compte et le jeton d’authentification.ASPSMS :
Dans les paramètres de votre compte, accédez à Userkey et copiez-le avec votre mot de passe autodéfini.Nous stockerons plus tard ces valeurs dans les variables
SMSAccountIdentification
etSMSAccountPassword
.Spécification de SenderID/Originator
Twilio:
Sous l’onglet Nombres , copiez votre numéro de téléphone Twilio.ASPSMS :
Dans le menu Déverrouiller les originateurs , déverrouillez un ou plusieurs originateurs ou choisissez un originateur alphanumérique (non pris en charge par tous les réseaux).Nous stockerons plus tard cette valeur dans la variable
SMSAccountFrom
.Transfert des informations d’identification du fournisseur SMS dans l’application
Mettent les informations d’identification et le numéro de téléphone de l’expéditeur à la disposition de l’application :
public static class Keys { public static string SMSAccountIdentification = "My Idenfitication"; public static string SMSAccountPassword = "My Password"; public static string SMSAccountFrom = "+15555551234"; }
Avertissement
Sécurité : ne stockez jamais de données sensibles dans votre code source. Le compte et les informations d’identification sont ajoutés au code ci-dessus pour simplifier l’exemple. Consultez le ASP.NET MVC de Jon Atten : Conserver les paramètres privés hors contrôle de code source.
Implémentation du transfert de données vers le fournisseur SMS
Configurez la
SmsService
classe dans le fichier App_Start\IdentityConfig.cs .Selon le fournisseur SMS utilisé, activez la section Twilio ou 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 } }
Exécutez l’application et connectez-vous avec le compte que vous avez précédemment inscrit.
Cliquez sur votre ID d’utilisateur, ce qui active la méthode d’action dans le
Index
Manage
contrôleur.Cliquez sur Ajouter.
En quelques secondes, vous recevrez un sms avec le code de vérification. Entrez-le et appuyez sur Envoyer.
La vue Gérer indique que votre numéro de téléphone a été ajouté.
Examiner le code
// 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);
}
La Index
méthode d’action dans Manage
le contrôleur définit le message status en fonction de votre action précédente et fournit des liens permettant de modifier votre mot de passe local ou d’ajouter un compte local. La Index
méthode affiche également l’état ou votre numéro de téléphone 2FA, les connexions externes, la 2FA activée et mémoriser la méthode 2FA pour ce navigateur (expliqué plus loin). Le fait de cliquer sur votre ID utilisateur (e-mail) dans la barre de titre ne transmet pas de message. Cliquez sur le lien Numéro de téléphone : supprimer passe Message=RemovePhoneSuccess
en tant que chaîne de requête.
https://localhost:44300/Manage?Message=RemovePhoneSuccess
[]
La AddPhoneNumber
méthode d’action affiche une boîte de dialogue pour entrer un numéro de téléphone pouvant recevoir des SMS.
// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
En cliquant sur le bouton Envoyer le code de vérification , le numéro de téléphone est affiché dans la méthode d’action 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 });
}
La GenerateChangePhoneNumberTokenAsync
méthode génère le jeton de sécurité qui sera défini dans le message SMS. Si le service SMS a été configuré, le jeton est envoyé en tant que chaîne « Votre code de sécurité est <jeton> ». La SmsService.SendAsync
méthode à est appelée de manière asynchrone, puis l’application est redirigée vers la VerifyPhoneNumber
méthode d’action (qui affiche la boîte de dialogue suivante), où vous pouvez entrer le code de vérification.
Une fois que vous avez entré le code et que vous avez cliqué sur Envoyer, le code est publié dans la méthode d’action 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);
}
La ChangePhoneNumberAsync
méthode vérifie le code de sécurité publié. Si le code est correct, le numéro de téléphone est ajouté au PhoneNumber
champ de la AspNetUsers
table. Si cet appel réussit, la SignInAsync
méthode est appelée :
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));
}
Le isPersistent
paramètre définit si la session d’authentification est conservée sur plusieurs demandes.
Lorsque vous modifiez votre profil de sécurité, un nouveau tampon de sécurité est généré et stocké dans le SecurityStamp
champ de la table AspNetUsers . Notez que le SecurityStamp
champ est différent du cookie de sécurité. Le cookie de sécurité n’est pas stocké dans la AspNetUsers
table (ou n’importe où ailleurs dans la base de données d’identité). Le jeton de cookie de sécurité est auto-signé à l’aide de DPAPI et est créé avec les UserId, SecurityStamp
informations de délai d’expiration et .
L’intergiciel de cookie vérifie le cookie à chaque demande. La SecurityStampValidator
méthode de la Startup
classe atteint la base de données et vérifie régulièrement l’empreinte de sécurité, comme spécifié avec le validateInterval
. Cela ne se produit que toutes les 30 minutes (dans notre exemple), sauf si vous modifiez votre profil de sécurité. L’intervalle de 30 minutes a été choisi pour réduire les déplacements vers la base de données.
La SignInAsync
méthode doit être appelée quand une modification est apportée au profil de sécurité. Lorsque le profil de sécurité change, la base de données met à jour le SecurityStamp
champ, et sans appeler la SignInAsync
méthode, vous restez connecté uniquement jusqu’à la prochaine fois que le pipeline OWIN atteint la base de données (le validateInterval
). Vous pouvez tester cela en modifiant la SignInAsync
méthode pour qu’elle retourne immédiatement et en définissant la propriété de cookie validateInterval
de 30 minutes à 5 secondes :
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);
Avec les modifications de code ci-dessus, vous pouvez modifier votre profil de sécurité (par exemple, en modifiant l’état de Two Factor Enabled) et vous serez déconnecté en 5 secondes lorsque la SecurityStampValidator.OnValidateIdentity
méthode échoue. Supprimez la ligne de retour dans la SignInAsync
méthode, modifiez un autre profil de sécurité et vous ne serez pas déconnecté. La SignInAsync
méthode génère un nouveau cookie de sécurité.
Activer l’authentification à deux facteurs
Dans l’exemple d’application, vous devez utiliser l’interface utilisateur pour activer l’authentification à deux facteurs (2FA). Pour activer 2FA, cliquez sur votre ID d’utilisateur (alias d’e-mail) dans la barre de navigation.
Cliquez sur Activer 2FA. Déconnectez-vous, puis reconnectez-vous. Si vous avez activé l’e-mail (voir mon tutoriel précédent), vous pouvez sélectionner le SMS ou l’e-mail pour 2FA. La page Vérifier le code s’affiche dans laquelle vous pouvez entrer le code (à partir d’un SMS ou d’un e-mail). Si vous cliquez sur la zone Mémoriser ce navigateur case activée, vous n’aurez pas besoin d’utiliser 2FA pour vous connecter avec cet ordinateur et ce navigateur. L’activation de la 2FA et le fait de cliquer sur Mémoriser ce navigateur vous offre une protection 2FA forte contre les utilisateurs malveillants qui tentent d’accéder à votre compte, tant qu’ils n’ont pas accès à votre ordinateur. Vous pouvez le faire sur n’importe quelle machine privée que vous utilisez régulièrement. En définissant Mémoriser ce navigateur, vous bénéficiez de la sécurité supplémentaire de 2FA à partir d’ordinateurs que vous n’utilisez pas régulièrement, et vous obtenez la commodité de ne pas avoir à passer par 2FA sur vos propres ordinateurs.
Comment inscrire un fournisseur d’authentification à deux facteurs
Lorsque vous créez un projet MVC, le fichier IdentityConfig.cs contient le code suivant pour inscrire un fournisseur d’authentification à deux facteurs :
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;
}
Ajouter un numéro de téléphone pour 2FA
La AddPhoneNumber
méthode d’action dans le Manage
contrôleur génère un jeton de sécurité et l’envoie au numéro de téléphone que vous avez fourni.
[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 });
}
Après avoir envoyé le jeton, il redirige vers la VerifyPhoneNumber
méthode d’action, où vous pouvez entrer le code pour inscrire des SMS pour 2FA. SMS 2FA n’est pas utilisé tant que vous n’avez pas vérifié le numéro de téléphone.
Activation de la 2FA
La EnableTFA
méthode d’action active 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");
}
Notez que doit SignInAsync
être appelé, car activer 2FA est une modification du profil de sécurité. Lorsque la 2FA est activée, l’utilisateur doit utiliser 2FA pour se connecter, à l’aide des approches 2FA qu’il a inscrites (SMS et e-mail dans l’exemple).
Vous pouvez ajouter d’autres fournisseurs 2FA tels que des générateurs de code QR ou écrire les vôtres.
Notes
Les codes 2FA sont générés à l’aide de l’algorithme de mot de passe à usage unique basé sur le temps et les codes sont valides pendant six minutes. Si vous prenez plus de six minutes pour entrer le code, vous obtiendrez un message d’erreur code non valide.
Combiner les comptes de connexion sociale et local
Vous pouvez combiner des comptes locaux et sociaux en cliquant sur votre lien de messagerie. Dans la séquence suivante, «RickAndMSFT@gmail.com » est d’abord créé en tant que connexion locale, mais vous pouvez d’abord créer le compte en tant que journal social, puis ajouter une connexion locale.
Cliquez sur le lien Gérer . Notez le 0 externe (connexions sociales) associé à ce compte.
Cliquez sur le lien vers un autre service de connexion et acceptez les demandes d’application. Les deux comptes ont été combinés. Vous pourrez vous connecter avec l’un ou l’autre compte. Vous voudrez peut-être que vos utilisateurs ajoutent des comptes locaux si leur service d’authentification de connexion sociale est en panne ou qu’ils ont probablement perdu l’accès à leur compte social.
Dans l’image suivante, Tom est un journal social (que vous pouvez voir dans la page Connexions externes : 1 ).
Cliquer sur Choisir un mot de passe vous permet d’ajouter un journal local associé au même compte.
Verrouillage de compte en cas d’attaques par force brute
Vous pouvez protéger les comptes de votre application contre les attaques par dictionnaire en activant le verrouillage des utilisateurs. Le code suivant dans la ApplicationUserManager Create
méthode configure le verrouillage :
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
Le code ci-dessus active le verrouillage pour l’authentification à deux facteurs uniquement. Bien que vous puissiez activer le verrouillage pour les connexions en remplaçant shouldLockout
par true dans la Login
méthode du contrôleur de compte, nous vous recommandons de ne pas activer le verrouillage pour les connexions, car cela rend le compte vulnérable aux attaques de connexion DOS . Dans l’exemple de code, le verrouillage est désactivé pour le compte d’administrateur créé dans la ApplicationDbInitializer Seed
méthode :
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);
}
}
Exiger qu’un utilisateur dispose d’un compte de messagerie validé
Le code suivant exige qu’un utilisateur dispose d’un compte de messagerie validé avant de pouvoir se connecter :
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);
}
}
Comment SignInManager vérifie l’exigence 2FA
La connexion locale et le journal social dans case activée pour voir si la 2FA est activée. Si 2FA est activé, la SignInManager
méthode d’ouverture de session retourne SignInStatus.RequiresVerification
et l’utilisateur est redirigé vers la SendCode
méthode d’action, où il doit entrer le code pour terminer la séquence de connexion. Si l’utilisateur a RememberMe est défini sur le cookie local de l’utilisateur, le SignInManager
retournera SignInStatus.Success
et ils n’auront pas à passer par 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 });
}
}
Le code suivant montre la SendCode
méthode d’action. Un SelectListItem est créé avec toutes les méthodes 2FA activées pour l’utilisateur. L’élément SelectListItem est passé à l’élément DropDownListFor helper, ce qui permet à l’utilisateur de sélectionner l’approche 2FA (généralement par e-mail et 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 });
}
Une fois que l’utilisateur publie l’approche 2FA, la HTTP POST SendCode
méthode d’action est appelée, le SignInManager
envoie le code 2FA et l’utilisateur est redirigé vers la VerifyCode
méthode d’action où il peut entrer le code pour terminer la connexion.
//
// 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 });
}
Verrouillage 2FA
Bien que vous puissiez définir le verrouillage de compte en cas d’échec de tentative de mot de passe de connexion, cette approche rend votre connexion vulnérable aux verrouillages DOS . Nous vous recommandons d’utiliser le verrouillage de compte uniquement avec 2FA. Lorsque le ApplicationUserManager
est créé, l’exemple de code définit le verrouillage 2FA et MaxFailedAccessAttemptsBeforeLockout
sur cinq. Une fois qu’un utilisateur se connecte (via un compte local ou un compte social), chaque tentative ayant échoué à 2FA est stockée et, si le nombre maximal de tentatives est atteint, l’utilisateur est verrouillé pendant cinq minutes (vous pouvez définir le temps de verrouillage avec DefaultAccountLockoutTimeSpan
).
Ressources supplémentaires
- Ressources recommandées pour l’identité ASP.NET Liste complète des blogs d’identité, des vidéos, des tutoriels et d’excellents liens SO.
- L’application MVC 5 avec Facebook, Twitter, LinkedIn et Google OAuth2 Sign-on montre également comment ajouter des informations de profil à la table des utilisateurs.
- ASP.NET MVC et Identity 2.0: Understanding the Basics par John Atten.
- Confirmation de compte et récupération de mot de passe avec ASP.NET Identity
- Introduction à ASP.NET Identity
- Annonce de RTM de ASP.NET Identity 2.0.0 par Pranav Rastogi.
- ASP.NET Identity 2.0 : Configuration de la validation de compte et de l’autorisation de Two-Factor par John Atten.