C# - 双因素身份验证(没有 ASP.NET 核心身份)

C# - Two-Factor Authentication (without ASP.NET Core Identity)

是否可以在不使用 ASP.NET 核心身份的情况下实施双因素身份验证?

我有自己的登录名 table,使用登录名 + 密码 + jwt 一切正常,但我的客户想通过短信或电子邮件使用双因素身份验证来提高安全性。

我想到了 Twilio 的 Authy,但我不明白如果没有 ASP.NET Core Identity 是否可行。

Twilio 的双因素身份验证 (2FA) 服务不依赖于 ASP.NET 核心身份框架,因此您无需将其与身份结合使用,但您可以。 在提供更多详细信息之前,我确实想了解 Twilio 的 2FA 产品的相同页面以避免混淆。

Twilio 有一个 couple of 2FA products as explained in this comparison page:

  1. Twilio Authy 应用程序:一个免费的消费者应用程序,用于生成 Time-based One-Time 密码 (TOTP)。
  2. Twilio Authy API:API 供开发人员将 2FA 集成到您的应用程序中。 Twilio Verify API 取代 Twilio Authy API.
  3. Twilio Verify API:API 供开发人员将 2FA 集成到您的应用程序中。与 Authy API.
  4. 相比,验证 API 具有改进的开发人员体验并支持更多渠道

Twilio 建议使用 Verify API 进行新开发,因此我将使用 Verify API 来回答这个问题,但是,让我知道您是否出于某种原因需要 Authy API 样本。


您可以将 Twilio 验证 API 添加到任何应用程序,例如,这是一个控制台应用程序来验证 phone 号码:

string accountSid = configuration["Twilio:AccountSid"];
string authToken = configuration["Twilio:AuthToken"];
string verificationServiceSid = configuration["Twilio:VerifyServiceSid"];

TwilioClient.Init(accountSid, authToken);

Console.Write("What is your phone number? ");
string phoneNumber = Console.ReadLine();

var verification = await VerificationResource.CreateAsync(
    to: phoneNumber,
    channel: "sms",
    pathServiceSid: verificationServiceSid
);

Console.WriteLine("Verification status: {0}", verification.Status);

Console.Write("Enter the code texted to your phone: ");
string code = Console.ReadLine();

var verificationCheck = await VerificationCheckResource.CreateAsync(
    verificationSid: verification.Sid,
    code: code,
    pathServiceSid: verificationServiceSid
);

Console.WriteLine("Verification status: {0}", verificationCheck.Status);

configuration 来自 ConfigurationBuilder,您可以了解更多关于 here

您可以在 Twilio Console 中找到 Twilio:AccountSidTwilio:AuthToken。 要使用验证 API,您需要创建一个验证服务,您也可以在 Twilio 控制台(或使用 API、SDK 或 CLI)中执行此操作。 验证服务将有一个字符串标识符 (SID),我将其存储在 Twilio:VerifyServiceSid.

一旦程序有了必要的 Twilio 配置,它就会初始化 TwilioClient,然后它会要求用户输入一个 phone 号码来验证,然后它使用 phone 号创建一个 VerificationResource。 创建此 VerificationResource 后,Twilio 将向 phone 号码发送代码。 该程序将请求该代码,然后使用该代码创建 VerificationCheckResource。 创建的状态 VerificationCheckResource 将指示代码是否正确。

Status 可以有三个不同的字符串值:

  1. 待处理:这是默认状态,也是提供代码但不是正确代码时的状态。
  2. 已通过:提供的代码正确,phone号码已通过验证。
  3. 已取消:验证已取消,您可以创建新的验证重新开始。

此示例使用 SMS 作为渠道,但其他渠道的流程大致相同。 上面的示例合并了来自 Twilio Docs on Verify for C# 的示例。


您现在可以将相同的逻辑应用到您认为合适的 ASP.NET 核心应用程序中。 这是一个 ASP.NET 核心 MVC 控制器的示例,其中包含一些操作和视图以通过短信验证 phone 号码。

HomeController.cs:

using Microsoft.AspNetCore.Mvc;
using Twilio.Rest.Verify.V2.Service;
using TwilioVerifyAspNet.Models;

public class HomeController : Controller
{
    private readonly string verifyServiceSid;

    public HomeController(IConfiguration configuration)
    {
        verifyServiceSid = configuration["Twilio:VerifyServiceSid"];
    }

    [HttpGet]
    public IActionResult Index() => View();
    
    [HttpGet]
    public IActionResult RequestTwoFactorToken() => View();
    
    [HttpPost]
    public async Task<IActionResult> RequestTwoFactorToken(RequestTwoFactorViewModel requestTwoFactorViewModel)
    {
        if (!ModelState.IsValid)
        {
            return View(requestTwoFactorViewModel);
        }

        var verificationResource = await VerificationResource.CreateAsync(
            to: requestTwoFactorViewModel.PhoneNumber,
            channel: "sms",
            pathServiceSid: verifyServiceSid
        );

        var verifyTwoFactorViewModel = new VerifyTwoFactorViewModel
        {
            VerificationSid = verificationResource.Sid,
            Code = null
        };
        return View("VerifyTwoFactorToken", verifyTwoFactorViewModel);
    }
    
    [HttpPost]
    public async Task<IActionResult> VerifyTwoFactorToken(VerifyTwoFactorViewModel verifyTwoFactorViewModel)
    {
        if (!ModelState.IsValid)
        {
            return View(verifyTwoFactorViewModel);
        }

        var verificationCheck = await VerificationCheckResource.CreateAsync(
            verificationSid: verifyTwoFactorViewModel.VerificationSid,
            code: verifyTwoFactorViewModel.Code,
            pathServiceSid: verifyServiceSid
        );

        switch (verificationCheck.Status)
        {
            case "pending":
                verifyTwoFactorViewModel.Status = "pending";
                return View("VerifyTwoFactorToken", verifyTwoFactorViewModel);
            case "canceled":
                verifyTwoFactorViewModel.Status = "canceled";
                return View("VerifyTwoFactorToken", verifyTwoFactorViewModel);
            case "approved":
                return View("Success");
        }
            
        return View("VerifyTwoFactorToken", verifyTwoFactorViewModel);
    }
}

RequestTwoFactorViewModel.cs:

using System.ComponentModel.DataAnnotations;

public class RequestTwoFactorViewModel
{
    [Required]
    public string PhoneNumber { get; set; }
}

VerifyTwoFactorViewModel.cs:

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;

public class VerifyTwoFactorViewModel
{
    [Required]
    public string? VerificationSid { get; set; }
    
    [Required]
    public string? Code { get; set; }

    // prevent user from setting the status via form
    [BindNever]
    public string? Status { get; set; }
}

RequestTwoFactorToken.cshtml:

@model RequestTwoFactorViewModel

<form asp-controller="Home" asp-action="RequestTwoFactorToken">
    <label for="phone-number">Phone Number</label> <br>
    <input id="phone-number" asp-for="PhoneNumber"/> <br>
    <button type="submit">Submit</button>
</form>

VerifyTwoFactorToken.cshtml:

@model VerifyTwoFactorViewModel

@if (Model.Status == null || Model.Status == "pending")
{
    <form asp-controller="Home" asp-action="VerifyTwoFactorToken">
        <input type="hidden" asp-for="VerificationSid" />
    
        <label for="code">Code</label> <br>
        <input id="code" asp-for="Code"/> <br>
        <button type="submit">Submit</button>
    </form>
}
else if (Model.Status == "cancelled")
{
    <text>Your 2FA verification has been cancelled</text>
}

您必须确保在应用程序启动时初始化 TwilioClientRequestTwoFactorToken HTTP GET 操作将呈现一个表单以请求 phone 号码。 提交表单时,RequestTwoFactorToken HTTP POST 操作将创建 VerifyResource,用于创建 VerifyTwoFactorViewModel 以呈现 VerifyTwoFactorToken.cshtml 视图。 VerifyTwoFactorToken.cshtml 视图要求用户提供 SMS 代码。 提交此表单时,VerifyTwoFactorToken 操作将创建一个 VerificationCheckResource。评估结果状态以查看代码是否正确。

这是一个示例,绝不是生产就绪,但它包含将其实现到您自己的应用程序中所需的所有部分。 希望这对您有所帮助,我迫不及待地想看看您构建了什么!