Bagikan melalui


Autentikasi dua faktor menggunakan SMS dan email dengan identitas ASP.NET

oleh Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi

Tutorial ini akan menunjukkan kepada Anda cara menyiapkan Autentikasi dua faktor (2FA) menggunakan SMS dan email.

Artikel ini ditulis oleh Rick Anderson (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung, dan Suhas Joshi. Sampel NuGet ditulis terutama oleh Hao Kung.

Topik ini mencakup hal-hal berikut:

Membuat sampel Identitas

Di bagian ini, Anda akan menggunakan NuGet untuk mengunduh sampel yang akan kami kerjakan. Mulailah dengan menginstal dan menjalankan Visual Studio Express 2013 untuk Web atau Visual Studio 2013. Instal Visual Studio 2013 Update 2 atau yang lebih tinggi.

Catatan

Peringatan: Anda harus menginstal Visual Studio 2013 Update 2 untuk menyelesaikan tutorial ini.

  1. Buat proyek Web ASP.NET kosong baru.

  2. Di Konsol Manajer Paket, masukkan perintah berikut:

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

    Dalam tutorial ini, kita akan menggunakan SendGrid untuk mengirim email dan Twilio atau ASPSMS untuk sms sms. Paket Identity.Samples menginstal kode yang akan kami kerjakan.

  3. Atur proyek untuk menggunakan SSL.

  4. Opsional: Ikuti instruksi dalam tutorial konfirmasi Email saya untuk menghubungkan SendGrid lalu jalankan aplikasi dan daftarkan akun email.

  5. Opsional: Hapus kode konfirmasi tautan email demo dari sampel (Kode ViewBag.Link di pengontrol akun. DisplayEmailForgotPasswordConfirmation Lihat metode dan tindakan dan tampilan pisau cukur ).

  6. Opsional:ViewBag.Status Hapus kode dari pengontrol Kelola dan Akun dan dari tampilan razor Views\Account\VerifyCode.cshtml dan Views\Manage\VerifyPhoneNumber.cshtml . Atau, Anda dapat menyimpan ViewBag.Status tampilan untuk menguji cara kerja aplikasi ini secara lokal tanpa harus menghubungkan dan mengirim pesan email dan SMS.

Catatan

Peringatan: Jika Anda mengubah salah satu pengaturan keamanan dalam sampel ini, aplikasi produksi harus menjalani audit keamanan yang secara eksplisit memanggil perubahan yang dibuat.

Menyiapkan SMS untuk autentikasi Dua faktor

Tutorial ini memberikan instruksi untuk menggunakan Twilio atau ASPSMS tetapi Anda dapat menggunakan penyedia SMS lainnya.

  1. Membuat Akun Pengguna dengan penyedia SMS

    Buat Twilio atau akun ASPSMS .

  2. Menginstal paket tambahan atau menambahkan referensi layanan

    Twilio:
    Di Package Manager Console, masukkan perintah berikut:
    Install-Package Twilio

    ASPSMS:
    Referensi layanan berikut perlu ditambahkan:

    Gambar jendela tambahkan referensi layanan

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

    Namespace:
    ASPSMSX2

  3. Mencari tahu kredensial Pengguna Penyedia SMS

    Twilio:
    Dari tab Dasbor akun Twilio Anda, salin SID Akun dan token Auth.

    ASPSMS:
    Dari pengaturan akun Anda, navigasikan ke Userkey dan salin bersama dengan Kata Sandi yang ditentukan sendiri.

    Kita nantinya akan menyimpan nilai-nilai ini dalam variabel SMSAccountIdentification dan SMSAccountPassword .

  4. Menentukan SenderID / Originator

    Twilio:
    Dari tab Nomor , salin nomor telepon Twilio Anda.

    ASPSMS:
    Dalam Menu Buka Kunci Originator , buka kunci satu atau beberapa Originator atau pilih Originator alfanumerik (Tidak didukung oleh semua jaringan).

    Kita nantinya akan menyimpan nilai ini dalam variabel SMSAccountFrom .

  5. Mentransfer kredensial penyedia SMS ke dalam aplikasi

    Buat kredensial dan nomor telepon pengirim tersedia untuk aplikasi:

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

    Peringatan

    Keamanan - Jangan pernah menyimpan data sensitif dalam kode sumber Anda. Akun dan kredensial ditambahkan ke kode di atas untuk menjaga sampel tetap sederhana. Lihat MVC ASP.NET Jon Atten: Jaga Pengaturan Privat Di Luar Kontrol Sumber.

  6. Implementasi transfer data ke penyedia SMS

    Konfigurasikan SmsService kelas dalam file App_Start\IdentityConfig.cs .

    Tergantung pada penyedia SMS yang digunakan, aktifkan Twilio atau bagian 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. Jalankan aplikasi dan masuk dengan akun yang sebelumnya Anda daftarkan.

  8. Klik ID Pengguna Anda, yang mengaktifkan Index metode tindakan di Manage pengontrol.

    Gambar akun terdaftar yang masuk ke aplikasi

  9. Klik Tambahkan.

    Gambar tautan tambahkan nomor telepon

  10. Dalam beberapa detik, Anda akan mendapatkan pesan teks dengan kode verifikasi. Masukkan dan tekan Kirim.

    Gambar memperlihatkan entri kode verifikasi telepon

  11. Tampilan Kelola memperlihatkan nomor telepon Anda telah ditambahkan.

    Gambar jendela tampilan kelola memperlihatkan nomor telepon

Memeriksa kode

// 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);
}

Metode Index tindakan di Manage pengontrol mengatur pesan status berdasarkan tindakan Anda sebelumnya dan menyediakan tautan untuk mengubah kata sandi lokal Anda atau menambahkan akun lokal. Metode ini Index juga menampilkan status atau nomor telepon 2FA Anda, login eksternal, 2FA diaktifkan, dan mengingat metode 2FA untuk browser ini (dijelaskan nanti). Mengklik ID pengguna (email) Anda di bilah judul tidak meneruskan pesan. Mengklik Nomor Telepon : menghapus tautan yang diteruskan Message=RemovePhoneSuccess sebagai string kueri.

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

[Gambar nomor telepon dihapus]

Metode AddPhoneNumber tindakan menampilkan kotak dialog untuk memasukkan nomor telepon yang dapat menerima pesan SMS.

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

Gambar kotak dialog tambahkan tindakan nomor telepon

Mengklik tombol Kirim kode verifikasi memposting nomor telepon ke metode tindakan 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 });
}

Metode ini GenerateChangePhoneNumberTokenAsync menghasilkan token keamanan yang akan diatur dalam pesan SMS. Jika layanan SMS telah dikonfigurasi, token dikirim sebagai string "Kode keamanan Anda adalah <token>". Metode SmsService.SendAsync untuk dipanggil secara asinkron, kemudian aplikasi dialihkan ke VerifyPhoneNumber metode tindakan (yang menampilkan dialog berikut), di mana Anda dapat memasukkan kode verifikasi.

Gambar kotak dialog verifikasi metode tindakan nomor telepon

Setelah Anda memasukkan kode dan klik kirim, kode diposting ke metode tindakan 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);
}

Metode ChangePhoneNumberAsync memeriksa kode keamanan yang diposting. Jika kode sudah benar, nomor telepon ditambahkan ke PhoneNumber bidang AspNetUsers tabel. Jika panggilan tersebut berhasil, SignInAsync metode ini disebut:

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));
}

Parameter isPersistent mengatur apakah sesi autentikasi dipertahankan di beberapa permintaan.

Saat Anda mengubah profil keamanan, stempel keamanan baru dibuat dan disimpan di SecurityStamp bidang tabel AspNetUsers . Perhatikan, SecurityStamp bidang ini berbeda dari cookie keamanan. Cookie keamanan tidak disimpan dalam AspNetUsers tabel (atau di tempat lain di DB Identitas). Token cookie keamanan ditandatangani sendiri menggunakan DPAPI dan dibuat dengan informasi waktu dan kedaluwarsa UserId, SecurityStamp .

Middleware cookie memeriksa cookie pada setiap permintaan. Metode SecurityStampValidator di Startup kelas mencapai DB dan memeriksa stempel keamanan secara berkala, seperti yang ditentukan dengan validateInterval. Ini hanya terjadi setiap 30 menit (dalam sampel kami) kecuali Anda mengubah profil keamanan Anda. Interval 30 menit dipilih untuk meminimalkan perjalanan ke database.

Metode SignInAsync ini perlu dipanggil ketika ada perubahan yang dilakukan pada profil keamanan. Ketika profil keamanan berubah, database memperbarui SecurityStamp bidang , dan tanpa memanggil SignInAsync metode , Anda akan tetap masuk hanya sampai kali berikutnya alur OWIN mencapai database ( validateInterval). Anda dapat menguji ini dengan mengubah SignInAsync metode untuk segera kembali, dan mengatur properti cookie validateInterval dari 30 menit ke 5 detik:

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);

Dengan perubahan kode di atas, Anda dapat mengubah profil keamanan Anda (misalnya, dengan mengubah status Dua Faktor Diaktifkan) dan Anda akan keluar dalam 5 detik ketika SecurityStampValidator.OnValidateIdentity metode gagal. Hapus baris pengembalian dalam SignInAsync metode , buat perubahan profil keamanan lain dan Anda tidak akan keluar. Metode ini SignInAsync menghasilkan cookie keamanan baru.

Mengaktifkan autentikasi dua faktor

Di aplikasi sampel, Anda perlu menggunakan UI untuk mengaktifkan autentikasi dua faktor (2FA). Untuk mengaktifkan 2FA, klik ID pengguna Anda (alias email) di bilah navigasi. Gambar U I untuk mengaktifkan autentikasi dua faktor
Klik aktifkan 2FA. Gambar setelah mengeklik pengguna I D memperlihatkan tautan pengaktifan autentikasi dua faktor Keluar, lalu masuk kembali. Jika Anda telah mengaktifkan email (lihat tutorial saya sebelumnya), Anda dapat memilih SMS atau email untuk 2FA. Gambar yang menampilkan opsi kirim verifikasi Halaman Verifikasi Kode ditampilkan di mana Anda dapat memasukkan kode (dari SMS atau email). Gambar halaman verifikasi kode Mengklik kotak centang Ingat browser ini akan membebaskan Anda dari kebutuhan untuk menggunakan 2FA untuk masuk dengan komputer dan browser tersebut. Mengaktifkan 2FA dan mengklik Ingat browser ini akan memberi Anda perlindungan 2FA yang kuat dari pengguna jahat yang mencoba mengakses akun Anda, selama mereka tidak memiliki akses ke komputer Anda. Anda dapat melakukan ini pada komputer privat apa pun yang Anda gunakan secara teratur. Dengan mengatur Ingat browser ini, Anda mendapatkan keamanan tambahan 2FA dari komputer yang tidak Anda gunakan secara teratur, dan Anda mendapatkan kenyamanan karena tidak harus melalui 2FA di komputer Anda sendiri.

Cara mendaftarkan penyedia autentikasi Dua faktor

Saat Anda membuat proyek MVC baru, file IdentityConfig.cs berisi kode berikut untuk mendaftarkan penyedia autentikasi Dua faktor:

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;
}

Menambahkan nomor telepon untuk 2FA

Metode AddPhoneNumber tindakan dalam Manage pengontrol menghasilkan token keamanan dan mengirimkannya ke nomor telepon yang telah Anda berikan.

[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 });
}

Setelah mengirim token, token dialihkan ke VerifyPhoneNumber metode tindakan, di mana Anda dapat memasukkan kode untuk mendaftarkan SMS untuk 2FA. SMS 2FA tidak digunakan sampai Anda telah memverifikasi nomor telepon.

Mengaktifkan 2FA

Metode EnableTFA tindakan memungkinkan 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");
}

Perhatikan bahwa SignInAsync harus dipanggil karena aktifkan 2FA adalah perubahan pada profil keamanan. Ketika 2FA diaktifkan, pengguna harus menggunakan 2FA untuk masuk, menggunakan pendekatan 2FA yang telah mereka daftarkan (SMS dan email dalam sampel).

Anda dapat menambahkan lebih banyak penyedia 2FA seperti generator kode QR atau Anda dapat menulis sendiri.

Catatan

Kode 2FA dihasilkan menggunakan Algoritma Kata Sandi Satu Kali Berbasis Waktu dan kode berlaku selama enam menit. Jika Anda membutuhkan waktu lebih dari enam menit untuk memasukkan kode, Anda akan mendapatkan pesan kesalahan kode yang tidak valid.

Menggabungkan akun masuk sosial dan lokal

Anda dapat menggabungkan akun lokal dan sosial dengan mengklik tautan email Anda. Dalam urutan berikut "RickAndMSFT@gmail.com" pertama kali dibuat sebagai login lokal, tetapi Anda dapat membuat akun sebagai log sosial terlebih dahulu, lalu menambahkan login lokal.

Gambar memilih tautan email

Klik tautan Kelola . Perhatikan 0 eksternal (login sosial) yang terkait dengan akun ini.

Gambar menampilkan halaman berikutnya dan memilih kelola

Klik tautan ke layanan masuk lain dan terima permintaan aplikasi. Kedua akun telah digabungkan, Anda akan dapat masuk dengan salah satu akun. Anda mungkin ingin pengguna menambahkan akun lokal jika layanan autentikasi masuk sosial mereka tidak berfungsi, atau kemungkinan besar mereka telah kehilangan akses ke akun sosial mereka.

Dalam gambar berikut, Tom adalah log masuk sosial (yang dapat Anda lihat dari Login Eksternal: 1 diperlihatkan di halaman).

Gambar yang menunjukkan login eksternal dan lokasi memilih kata sandi

Mengklik Pilih kata sandi memungkinkan Anda menambahkan log lokal yang terkait dengan akun yang sama.

Gambar memilih halaman kata sandi

Penguncian akun dari serangan brute force

Anda dapat melindungi akun di aplikasi Anda dari serangan kamus dengan mengaktifkan penguncian pengguna. Kode berikut dalam ApplicationUserManager Create metode mengonfigurasi penguncian:

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

Kode di atas hanya memungkinkan penguncian untuk autentikasi dua faktor. Meskipun Anda dapat mengaktifkan penguncian untuk masuk dengan mengubah shouldLockout ke true dalam Login metode pengontrol akun, kami sarankan Anda tidak mengaktifkan penguncian untuk masuk karena membuat akun rentan terhadap serangan masuk DOS . Dalam kode sampel, penguncian dinonaktifkan untuk akun admin yang dibuat dalam ApplicationDbInitializer Seed metode :

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);
    }
}

Mengharuskan pengguna memiliki akun email yang divalidasi

Kode berikut mengharuskan pengguna memiliki akun email yang divalidasi sebelum mereka dapat masuk:

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);
    }
}

Cara SignInManager memeriksa persyaratan 2FA

Masuk lokal dan masuk sosial untuk melihat apakah 2FA diaktifkan. Jika 2FA diaktifkan, SignInManager metode masuk mengembalikan SignInStatus.RequiresVerification, dan pengguna akan dialihkan ke SendCode metode tindakan, di mana mereka harus memasukkan kode untuk menyelesaikan log secara berurutan. Jika pengguna memiliki RememberMe diatur pada cookie lokal pengguna, SignInManager akan kembali SignInStatus.Success dan mereka tidak harus melalui 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 });
    }
}

Kode berikut menunjukkan SendCode metode tindakan. SelectListItem dibuat dengan semua metode 2FA diaktifkan untuk pengguna. SelectListItem diteruskan ke pembantu DropDownListFor, yang memungkinkan pengguna untuk memilih pendekatan 2FA (biasanya email dan 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 });
}

Setelah pengguna memposting pendekatan 2FA, HTTP POST SendCode metode tindakan dipanggil, SignInManager mengirim kode 2FA, dan pengguna dialihkan ke VerifyCode metode tindakan di mana mereka dapat memasukkan kode untuk menyelesaikan masuk.

//
// 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 });
}

Penguncian 2FA

Meskipun Anda dapat mengatur penguncian akun pada kegagalan upaya kata sandi masuk, pendekatan tersebut membuat login Anda rentan terhadap penguncian DOS . Kami sarankan Anda menggunakan penguncian akun hanya dengan 2FA. ApplicationUserManager Saat dibuat, kode sampel mengatur penguncian 2FA dan MaxFailedAccessAttemptsBeforeLockout ke lima. Setelah pengguna masuk (melalui akun lokal atau akun sosial), setiap upaya yang gagal di 2FA disimpan, dan jika upaya maksimum tercapai, pengguna dikunci selama lima menit (Anda dapat mengatur waktu penguncian dengan DefaultAccountLockoutTimeSpan).

Sumber Daya Tambahan