nodejs-two-factor-authentication-practice-trungquandev-feature-img

Xin chào tất cả các bạn, mình là Quân, hôm nay chúng ta sẽ cùng nhau đi tìm hiểu một tính năng bảo mật rất thú vị và phổ biến trong các ứng dụng ngày nay đó là xác thực bảo mật 2 lớp – Two-Factor Authentication (2FA) cũng như làm một cái ứng dụng thực tiễn Demo 2FA chuyên nghiệp bằng NodeJS nhé.

“Bài này nằm trong loạt bài Lập Trình NodeJS từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com

Những nội dung có trong bài:

  1. Đào sâu vào lý thuyết một chút
  2. Phân tích & triển khai code ứng dụng 2FA với NodeJS
  3. Demo ứng dụng bảo mật 2 lớp – 2FA Authentication
  4. Chia sẻ full toàn bộ Source Code của bài hôm nay trên Github

1. Đào sâu vào lý thuyết một chút

Vẫn là phong cách quen thuộc của mình, tuy rằng việc đọc lý thuyết khá dễ buồn ngủ nhưng mà trước khi lao đầu vào code thì chúng ta nên phân tích, tìm hiểu kỹ lý thuyết một chút, để có thể hiểu sâu bản chất của việc mà chúng ta đang làm là gì nhé.

Phần lý thuyết này mình sẽ giải thích cho các bạn một cách cực kỳ dễ hiểu về những loại thuật ngữ sau:
Two-Factor Authentication (2FA)
One-time Password (OTP)
HMAC-based One-Time Password (HOTP)
Time-based One-time Password (TOTP)

Chắc hẳn đa phần các bạn đều đã từng gặp trường hợp đăng nhập vào tài khoản facebook hoặc google thì sau bước nhập email + password, các bạn vẫn phải nhập thêm một cái mã token (thường là 6 chữ số và có thời gian hiệu lực 30 giây -> 1 phút) thì mới có thể đăng nhập được vào tài khoản. Có nhiều cách để nhận được mã này ví dụ như thông qua email, số điện thoại hoặc là được gennerate từ một ứng dụng tạo mã bên thứ ba như Google Authenticator hoặc Authy. Hình thức bảo mật trên được gọi là Xác thực 2 lớp – Two-Factor Authentication.

nodejs-two-factor-authentication-practice-trungquandev-01

Vậy về mặt lý thuyết thì Two-Factor Authentication là gì?

Two-Factor Authentication (thường viết tắt là 2FA hoặc TFA) là một phương pháp xác thực người dùng dựa trên 2 yếu tố, một là mật khẩu (thứ phổ biến nhất) và thứ hai là một thứ mà người dùng sở hữu, có quyền truy cập đến, ví dụ như dấu vân tay, tin nhắn SMS, gửi mã token tới Email hoặc tốt hơn nữa là One-time Password (OTP) (mật khẩu một lần có giới hạn hiệu lực theo thời gian).

One-Time Password sẽ là thứ mà chúng ta bàn đến trong ngày hôm nay, SMSEmail thì mình sẽ làm ở những bài riêng biệt khác sau. Và đúng như cái tên của nó, One-time Password hay còn được viết tắt là OTP là một loại mã token mà chỉ có thể được sử dụng một lần rồi sau đó nó sẽ bị hủy, không được phép sử dụng tới lần thứ hai.

Lúc này, câu hỏi tiếp theo mà chúng ta đặt ra ở đây là, làm sao để đảm bảo chúng ta có thể tạo một mã token mà thỏa mãn điều kiện là duy nhất (unique)?

Và câu trả lời sẽ tiếp tục dẫn dắt chúng ta đến với một khái niệm khác đó là: Thuật toán HMAC-based One-Time Password, hay còn được viết tắt là HOTP.

HMAC-based One-Time PasswordHOTP là một thuật toán sinh mã OTP dựa trên hàm băm HMAC_SHA-1, nó sử dụng 2 thành phần: thứ nhất là một Chuỗi Secret cố định, còn thành phần thứ hai là một bộ đếm (Counter) bộ đếm này dùng một cái là “Moving-Factor” (mình tạm dịch ra là một yếu tố di chuyển, các bạn cũng có thể coi nó tương tự một chuỗi random ngẫu nhiên cho dễ hiểu cũng được.)

Để mà đi sâu vào cái HOTP trên thì nó lại là cả một bầu trời kiến thức về thuật toán khác, mình sẽ để link chính thức ở đây cho các bạn tham khảo thêm rồi tập trung tiếp vào nội dung của chúng ta nhé.
https://tools.ietf.org/html/rfc4226

– Tiếp theo, vì kết quả output của hàm băm HMAC_SHA-1 ở trên là một giá trị có độ dài 160 bit = 20 bytes nên chúng ta sẽ cần thêm một bước làm ngắn gọn output để mắt người dùng có thể dễ dàng đọc được. Việc cắt ngắn này sẽ dẫn chúng ta đến với thuật toán TOTP – Time-based One-time Password algorithm để tạo ra chuỗi có độ dài như chúng ta mong muốn, ví dụ "170 795" mà các bạn thường thấy ở app Google Authenticator hoặc Authy.

Time-based One-time Password – TOTP về cơ bản chỉ khác HOTP ở chỗ là TOTP sẽ sử dụng “thời gian” (Time) để làm bộ đếm (Counter) thay vì “Moving Factor” như HOTP. Chính vì việc sử dụng counterthời gian nên phía Server lẫn Client khi đã có chung Secret Key rồi thì không cần có sự tương tác qua lại nữa. Vì cả 2 phía đều có quyền truy cập vào thời gian. Điều này cũng trả lời luôn cho một thắc mắc khá thú vị mà lâu nay mình vẫn tự hỏi, đó là tại sao khi mình thử tắt mạng, không kết nối internet cho cái điện thoại vậy mà Token sinh ra của mấy cái app Google Authenticator hay Authy vẫn sử dụng được ngon lành chả vấn đề gì =))

Giải thích cụ thể hơn cho các bạn đó là: phía Server sẽ so sánh giá trị token mà người dùng submit từ phía client lên với tất cả các token được sinh ra trong cùng một khoảng thời gian nhất định trên Server. (thường là 30 giây cho đến 1 phút), và dĩ nhiên là nếu trùng nhau thì bạn sẽ pass qua vòng xác thực 2 lớp này. Đọc đến đây nhiều bạn có thể sẽ thắc mắc tiếp là: Ủa thế server và client khác múi giờ (Time zone) thì làm sao mà khoảng thời gian của 2 phía có thể đồng nhất được nhỉ?
Giải pháp là chúng ta có thể convert thời gian của cả 2 phía về dạng Unix Timestamp (hay còn gọi với tên khác là Epoch Time) rồi so sánh chúng. Hiểu một cách đơn giản thì Unix Timestamp là số giây đếm tăng dần từ một điểm thời gian cố định trong quá khứ đó là ngày 01/01/1970 (UTC) 00:00:00
Mình sẽ để link tham khảo về Unix Time cho các bạn tìm hiểu thêm ở đây https://www.unixtimestamp.com/

Mình cũng sẽ để link tài liệu chính thức của TOTP cho các bạn nào muốn tìm hiểu chuyên sâu về thuật toán nhé, chứ nói thật viết ra mớ lý thuyết dài dòng theo ý hiểu của mình tới tận khúc này là mình cũng oải + buồn ngủ lắm rồi =)))
https://tools.ietf.org/html/rfc6238

Và tiếp theo, tới phần thú vị đỡ buồn ngủ hơn rồi, chúng ta sẽ đi code một ví dụ demo bảo mật 2 lớp với NodeJS như tiêu đề của bài viết nhé. Mình có làm giao diện ứng dụng rất sát với thực tế cho các bạn dễ hình dung đấy, cứ kéo xuống dưới là thấy 😀

(Ngoài lề quen thuộc: Cảnh báo này dành cho mấy bạn admin của mấy trang TopDev, TechBlog… chuyên đi copy rồi xào bài, hoặc bất kể trang nào khác mà đã đi copy bài không phải của các bạn thì hãy tôn trọng người viết bài chân chínhtuyệt đối không được xào nấu, chỉnh sửa linh tinh bài viết của mình, cấm xóa những liên kết (link) trong bài của mình cũng như tự ý xóa các câu thoại của mình trong toàn bộ bài viết rồi post lại lên trang của các bạn như kiểu đây là bài của các bạn vậy, mình sẽ thường xuyên dùng tool để check, và nếu phát hiện ra thì cứ đơn giản là chắc chắn sẽ ăn report DMCA nhé.)


2. Phân tích & triển khai code ứng dụng 2FA với NodeJS

Trước khi vào code, mình đã phân tích cũng như vẽ lại workflow tổng quát của ứng dụng cho các bạn dễ hình dung nhất như thế này:

nodejs-two-factor-authentication-practice-workflow-trungquandev-02

Cấu trúc thư mục dự án của chúng ta sẽ trông như sau:

src

controllers

HomeController.js

AuthController.js

helpers

2fa.js

routes

api.js

views

home.html

enable2FA.html

login.html

verify2FA.html

server.js

package.json


Việc khởi tạo ứng dụng nodejs thì mình không làm lại, các bạn có thể xem cách làm ở các bài viết trước của mình tại link bên dưới đây:
https://trungquandev.com/series-lap-trinh-nodejs/

Tiếp theo, trong ví dụ ngày hôm nay, chúng ta sẽ cài đặt 3 module là: express, otplibqrcode

npm install --save express otplib qrcode

Một lưu ý nhỏ nhưng khá quan trọng nữa, code NodeJS trong bài này mình viết là Javascript ES Modules, nên các bạn sẽ thấy có cú pháp import – export thay vì require và module.exports của CommonJS thông thường. Và để code chạy được thì yêu cầu máy các bạn cần phiên bản nodejs tối thiểu là v12.0.0 trở lên. Cụ thể ở thời điểm mình viết bài này thì bản NodeJS LTS là 12.18.3, còn máy của mình thì mình đang dùng bản gần mới nhất là 14.7.0

Các bạn có thể tham khảo bài viết sau của mình về việc Quản lý (upgrade/downgrade) phiên bản NodeJS dễ dàng trên mọi hệ điều hành {MacOS, Linux, Window} nhé.

Và bài viết hướng dẫn cách sử dụng ES Modules – cú pháp import/export trong NodeJS.

Mình sẽ hướng dẫn lần lượt các file code như sau:

File src/helpers/2fa.js

Trong file này mình sử dụng module otplib để viết 3 function đảm nhiệm 3 chức năng độc lập:

  • generateUniqueSecret: Tạo mã Secret Key.
  • generateOTPToken: Tạo mã OTP token.
  • verifyOTPToken: Xác thực (verify) mã OTP token.

Và module qrcode để tạo mã QR gửi về phía client cho user quét mã:

  • generateQRCode: Tạo mã QR Code
/**
 * Created by trungquandev.com's author on 08/10/2020.
 * src/helpers/2fa.js
 */
import qrcode from 'qrcode'
import otplib from 'otplib'

/** Gọi ra để sử dụng đối tượng "authenticator" của thằng otplib */
const { authenticator } = otplib

/** Tạo secret key ứng với từng user để phục vụ việc tạo otp token.
  * Lưu ý: Secret phải được gen bằng lib otplib thì những app như
    Google Authenticator hoặc tương tự mới xử lý chính xác được.
  * Các bạn có thể thử để linh linh cái secret này thì đến bước quét mã QR sẽ thấy có lỗi ngay.
*/
const generateUniqueSecret = () => {
  return authenticator.generateSecret()
}

/** Tạo mã OTP token */
const generateOTPToken = (username, serviceName, secret) => {
  return authenticator.keyuri(username, serviceName, secret)
}

/** Kiểm tra mã OTP token có hợp lệ hay không
 * Có 2 method "verify" hoặc "check", các bạn có thể thử dùng một trong 2 tùy thích.
*/
const verifyOTPToken = (token, secret) => {
  return authenticator.verify({ token, secret })
  // return authenticator.check(token, secret)
}

/** Tạo QR code từ mã OTP để gửi về cho user sử dụng app quét mã */
const generateQRCode = async (otpAuth) => {
  try {
    const QRCodeImageUrl = await qrcode.toDataURL(otpAuth)
    return `<img src='${QRCodeImageUrl}' alt='qr-code-img-trungquandev' />`
  } catch (error) {
    console.log('Could not generate QR code', error)
    return
  }
}

export {
  generateUniqueSecret,
  verifyOTPToken,
  generateOTPToken,
  generateQRCode,
}

File src/routes/api.js

File này sẽ định nghĩa toàn bộ các API Endpoints trong ứng dụng của chúng ta (sử dụng framework express), và gọi đến các controllers tương ứng.

/**
 * Created by trungquandev.com's author on 08/10/2020.
 * src/routes/api.js
 */
import express from 'express'
import {
  getLoginPage,
  postLogin,
  getEnable2FAPage,
  postEnable2FA,
  getverify2FAPage,
  postVerify2FA,
} from '../controllers/AuthController.js'
import { getHomePage } from '../controllers/HomeController.js'

const router = express.Router()

/**
 * Init all APIs on your application
 * @param {*} app from express framework
 */
const initAPIs = (app) => {
  // Gọi ra trang chủ home page
  router.get('/', getHomePage)

  // Trang login
  router.get('/login', getLoginPage)
  router.post('/login', postLogin)

  // Trang bật tính năng bảo mật 2 lớp
  router.get('/enable-2fa', getEnable2FAPage)
  router.post('/enable-2fa', postEnable2FA)

  // Trang yêu cầu xác thực 2 lớp
  router.get('/verify-2fa', getverify2FAPage)
  router.post('/verify-2fa', postVerify2FA)

  return app.use('/', router)
}

export { initAPIs }

File src/controllers/HomeController.js

File này khá đơn giản, chỉ có một controller để gọi tra trang chủ home page. Mình chỉ sử dụng module path lấy đường dẫn file html để trả về client.

/**
 * Created by trungquandev.com's author on 08/10/2020.
 * src/controllers/HomeController.js
 */
import path from 'path'

// Lấy đường dẫn thư mục gốc của ứng dụng
const __dirname = path.resolve()

/** controller get home page */
const getHomePage = async (req, res) => {
  return res.sendFile(path.join(`${__dirname}/src/views/home.html`))
}

export {
  getHomePage,
}

File src/controllers/AuthController.js

File này sẽ chứa toàn bộ các controllers có liên quan đến xác thực – Authentication. Chức năng của từng function thì mình cũng có comment rất chi tiết trong code như những bài viết khác theo phong cách quen thuộc của mình rồi. Ngoài ra mình có một số lưu ý muốn giải thích cho các bạn trong file này như sau:

  • const MOCK_USER: đây là biến mà mình tạo ra với mục đích giả lập thông tin một user, phục vụ cho việc demo ứng dụng, nếu là dự án thực tế thì dĩ nhiên thông tin này sẽ PHẢI đảm bảo là được lưu vào cơ sở dữ liệu nhé.
  • Trong thông tin MOCK_USER này có 2 trường usernamepassword lát nữa mình dùng để đăng nhập, is2FAEnabled là trường để kiểm tra xem user đó đã bật xác thực hai lớp cho tài khoản chưa?
  • Và cuối cùng quan trọng nhất secret: đây là mã secret mà chúng ta sẽ generate bởi thằng otplibtrên code helpers/2fa.js ban nãy. Mỗi một User sẽ đi kèm với một mã secret duy nhất, và mã secret này cũng bắt buộc phải lưu tương ứng với thông tin của user đó trong cơ sở dữ liệu tùy theo thiết kế Database của các bạn nhé.
    Nói chung là PHẢI lưu secret lại, còn đối với code ví dụ của mình thì mỗi lần tắt code đi chạy lại thì thằng MOCK_USER kia sẽ lại được làm mới một mã secret khác nhé.
/**
 * Created by trungquandev.com's author on 08/10/2020.
 * src/controllers/AuthController.js
 */
import path from 'path'
import {
  generateUniqueSecret,
  verifyOTPToken,
  generateOTPToken,
  generateQRCode,
} from '../helpers/2fa.js'

// Lấy đường dẫn thư mục gốc của ứng dụng
const __dirname = path.resolve()

/** Vì demo nên mình sẽ tạo một biến giả lập user ở global của file.
 * Trong dự án thực tế, user và secret riêng của user đó PHẢI được lưu vào Database
*/
const MOCK_USER = {
  username: 'trungquandev',
  password: 'trungquandev',
  is2FAEnabled: true,
  secret: generateUniqueSecret()
}

/** controller get login page */
const getLoginPage = async (req, res) => {
  return res.sendFile(path.join(`${__dirname}/src/views/login.html`))
}

/** controller get enable 2FA page */
const getEnable2FAPage = async (req, res) => {
  return res.sendFile(path.join(`${__dirname}/src/views/enable2FA.html`))
}

/** controller get verify 2FA page */
const getverify2FAPage = async (req, res) => {
  return res.sendFile(path.join(`${__dirname}/src/views/verify2FA.html`))
}

/** controller xử lý đăng nhập */
const postLogin = async (req, res) => {
  try {
    let user = MOCK_USER
    const { username, password } = req.body
    // Giả sử trường hợp đăng nhập thành công
    if (username === user.username && password === user.password) {
      // Thực hiện yêu cầu xác thực 2 bước nếu tài khoản user này đã bật xác thực 2 lớp trước đó.
      if (user.is2FAEnabled) {
        return res.status(200).json({
          isCorrectIdentifier: true,
          is2FAEnabled: true,
          isLoggedIn: false,
        })
      }
      // Bỏ qua xác thực 2 lớp nếu tài khoản user này không bật xác thực 2 lớp
      return res.status(200).json({
        isCorrectIdentifier: true,
        is2FAEnabled: false,
        isLoggedIn: true,
      })
    }
    // Trường hợp đăng nhập thất bại (do thông tin đăng nhập không chính xác)
    return res.status(200).json({
      isCorrectIdentifier: false,
      is2FAEnabled: false,
      isLoggedIn: false,
    })
  } catch (error) {
    return res.status(500).json(error)
  }
}

/** controller xử lý tạo mã otp và gửi về client dạng hình ảnh QR Code */
const postEnable2FA = async (req, res) => {
  try {
    let user = MOCK_USER

    // đây là tên ứng dụng của các bạn, nó sẽ được hiển thị trên app Google Authenticator hoặc Authy sau khi bạn quét mã QR
    const serviceName = 'trungquandev.com'
    // Thực hiện tạo mã OTP
    const otpAuth = generateOTPToken(user.username, serviceName, user.secret)
    // console.log(otpAuth)
    // nếu các bạn console.log cái otpAuth ở trên thì các bạn sẽ thấy rõ hơn về nó, mình ví dụ:
    // otpauth://totp/trungquandev.com:trungquandev?secret=GYCCWGRLDY3RAFBU&period=30&digits=6&algorithm=SHA1&issuer=trungquandev.com

    // Tạo ảnh QR Code để gửi về client
    const QRCodeImage = await generateQRCode(otpAuth)

    return res.status(200).json({ QRCodeImage })
  } catch (error) {
    return res.status(500).json(error)
  }
}

const postVerify2FA = async (req, res) => {
  try {
    let user = MOCK_USER
    const { otpToken } = req.body

    // Kiểm tra mã token người dùng truyền lên có hợp lệ hay không?
    const isValid = verifyOTPToken(otpToken, user.secret)
    /** Sau bước này nếu verify thành công thì thực tế chúng ta sẽ redirect qua trang đăng nhập thành công,
    còn hiện tại demo thì mình sẽ trả về client là đã verify success hoặc fail */

    return res.status(200).json({ isValid })
  } catch (error) {
    return res.status(500).json(error)
  }
}

export {
  getLoginPage,
  getEnable2FAPage,
  getverify2FAPage,
  postLogin,
  postEnable2FA,
  postVerify2FA,
}

4 files giao diện html
src/views/home.html
src/views/enable2FA.html
src/views/login.html
src/views/verify2FA.html

Riêng đối với 4 file html ở trên, bởi vì chúng nó thật sự rất dài để mà nhúng code vào bài viết này nên mình sẽ để link Github riêng đến từng file cho các bạn click vào xem. Do là mình custom css hơi nhiều, vì demo giao diện xấu mình không chịu được =)))

Các bạn chỉ cần để ý quan trọng nhất là ở phần đầu của mỗi file, ngay phía trên thẻ mở <body> thì mình có viết mã javascript để gọi tới các api cũng như xử lý giao diện sau mỗi lần gọi. Gọi api thì mình dùng axios, DOM các phần tử thì mình dùng jquery, và mấy cái còn lại như bootstrap, fontawesome, sweetalert2 để làm đẹp trang web. Tất cả các thư viện này mình đều import về thông qua CDN, nên là nhớ đảm bảo máy bạn phải có internet thì chúng mới hoạt động được nhé.

Cuối cùng là src/server.js

Vẫn là phong cách đặt tên quen thuộc của mình, khởi tạo file server.js để chạy ứng dụng, sử dụng framework ExpressJS:

/**
 * Created by trungquandev.com's author on 08/10/2020.
 * src/server.js
 */
import express from 'express'
import { initAPIs } from './routes/api.js'

const app = express()

// Cho phép lý dữ liệu từ form method POST
app.use(express.urlencoded({extended: true}))
app.use(express.json())

// Khởi tạo các APIs cho ứng dụng
initAPIs(app)

// chọn một port mà bạn muốn và sử dụng để chạy ứng dụng tại local
const port = 8017
app.listen(port, () => {
  console.log(`Hello trungquandev.com, I'm running at localhost:${port}/`)
}) 

3. Demo ứng dụng bảo mật 2 lớp – 2FA Authentication

Trước khi vào demo, mình cần các bạn có một chiếc điện thoại smart phone, rồi cài cho mình 2 cái app Google AuthenticatorAuthy mà mình đã nhắc từ đầu bài viết nhé.

Lý do mình muốn các bạn cài cả hai cái app trên là vì muốn để các bạn tự trải nghiệm chúng rồi chọn một cái mà các bạn thích, chúng nó đều nổi tiếng cả.

  • Đối với Google Authenticator thì miễn bàn rồi, hàng của google mà, nhưng bị một cái khá bất tiện là nếu các bạn không may bị mất điện thoại hoặc đổi điện thoại thì sẽ rất phiền trong việc cấu hình lại bảo mật 2 lớp trên các tài khoản mà các bạn đang sử dụng. Bởi vì google không lưu Secret key của bạn trên hệ thống của họ – việc này là hoàn toàn đúng.
  • Còn với Authy, bạn sẽ tạo một tài khoản trên hệ thống của họ theo số điện thoạiemail, thằng Authy này sẽ lưu Secret key của các tài khoản mà bạn đang sử dụng bảo mật 2 lớp. Chính vì thế nên dù có mất hay là đổi điện thoại thì chỉ cần đăng nhập lại vào Authy là các bạn có thể dùng bình thường không vấn đề gì. (Cá nhân mình thì mình chọn Authy để dùng.)

Bây giờ chúng ta sẽ chạy ứng dụng lên: node src/server.js
Kết quả đầu tiên sẽ hiện lên màn hình là trang home page “/” có dẫn link tới các page con:

nodejs-two-factor-authentication-practice-trungquandev-03

Trang bật xác thực 2 lớp “/enable-2fa”: sau khi nhấn button Enable bên dưới thì các bạn sẽ nhận được mã QR Code.

nodejs-two-factor-authentication-practice-trungquandev-04

Sử dụng Google AuthenticatorAuthy để quét mã QR ở trên. Lưu ý: như mình đã nói ở phần hai của bài viết, vì Secret Key mà mình demo nó đang được làm mới mỗi khi các bạn tắt server đi bật lại, nên mã QR Code này chỉ đúng trong một phiên xử lý của chúng ta thôi nhé. Nếu có lỡ tắt server đi bật lại thì các bạn sẽ phải tạo lại QR Codequét lại mã để lấy đúng Token mới.

nodejs-two-factor-authentication-practice-trungquandev-05
nodejs-two-factor-authentication-practice-trungquandev-06

Tiếp theo là trang đăng nhập “/login”: các bạn sử dụng đúng username, password đều là “trungquandev” như trong AuthController của mình có note nhé.

nodejs-two-factor-authentication-practice-trungquandev-07

Mình có làm tính năng check tài khoản nếu chính xác và tài khoản có bật xác thực 2 lớp is2FAEnabled thì sẽ redirect qua trang verify token:

nodejs-two-factor-authentication-practice-trungquandev-08

Màn hình nhập mã OTP Token để kiểm tra “/verify-2fa” lưu ý nhập đúng mã trên app Google Authenticator hoặc Authy của các bạn nhé:

nodejs-two-factor-authentication-practice-trungquandev-09

Sau khi kiểm tra token đúng hoặc sai thì mình sẽ demo thông báo về như thế này:

nodejs-two-factor-authentication-practice-trungquandev-11
nodejs-two-factor-authentication-practice-trungquandev-10

4. Chia sẻ full toàn bộ Source Code của bài hôm nay trên Github

Vậy là kết thúc bài hôm nay, mình đã hướng dẫn đầy đủ từ lý thuyết cho đến thực hành code về việc xác thực bảo mật 2 lớp và ứng dụng code với NodeJS

Mình có để full source code của bài hôm nay ở repo này cho các bạn tham khảo giống như mọi lần, các bạn có thể clone về và làm theo các bước hướng dẫn mà mình có để trong file README nhé, chỉ có vài bước khá dễ dàng thôi.

Và nếu thấy bài viết bổ ích, hãy ủng hộ về mặt tinh thần bằng cách cho mình 1 star trên repo này để mình có động lực tiếp tục viết những bài viết chất lượng nha, cảm ơn các bạn.
https://github.com/trungquandev/nodejs-two-factor-authentication-2fa


Cảm ơn các bạn đã dành thời gian đọc bài viết.

Xin chào và hẹn gặp lại các bạn ở những bài viết tiếp theo.

Best Regards – Trung Quân – Green Cat


Tham khảo kiến thức:

https://www.npmjs.com/package/otplib

https://www.npmjs.com/package/qrcode

https://blog.soshace.com/implementing-two-factor-authentication-with-nodejs-and-otplib/

“Thanks for awesome knowledges.”

Khóa học lập trình làm việc thực tế:

Nếu các bạn thấy bài viết của mình có ích thì hãy ủng hộ mình bằng cách tham khảo bài viết giới thiệu khóa học cực kỳ chất lượng và chính chủ dưới đây của mình nhé, cảm ơn các bạn ^^

nodejs-mongodb-messenger-realtime-course-trungquandev
Node.js và MongoDB - Xây dựng một ứng dụng Messenger trò chuyện trực tuyến.