Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
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.SamplesDans ce tutoriel, nous allons utiliser SendGrid pour envoyer des messages électroniques et Twilio ou ASPSMS pour les SMS. Le
Identity.Samplespackage 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.Linkdans le contrôleur de compte. Consultez les méthodes d’actionDisplayEmailet etForgotPasswordConfirmationles vues razor ).Optionnel: Supprimez le
ViewBag.Statuscode 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.Statusfonctionnement 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 TwilioASPSMS :
La référence de service suivante doit être ajoutée :
Adresse :
https://webservice.aspsms.com/aspsmsx2.asmx?WSDLEspace de noms :
ASPSMSX2Identification 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
SMSAccountIdentificationetSMSAccountPassword.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
SmsServiceclasse 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
IndexManagecontrô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.RequiresVerificationet 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.