0

Xác thực 2 lớp trong Rails với Devise và Nexmo

Vào năm 2015, hơn 150 triệu hồ sơ người dùng đã bị đánh cắp và rò rỉ dữ liệu đã cho thấy mọi người vẫn có xu hướng sử dụng chung mật khẩu cho các trang web khác nhau. Vì vậy, để gia tăng an toàn bảo mật cho tài khoản, các ứng dụng đã sử dụng cơ chế xác thực hai lớp Two-factor authentication hay còn được gọi 2FA. Mặc dù vẫn có nguy cơ bị hack , nhưng cách xác thực này đã tăng rất nhiều tính an toàn cho tài khoản người dùng.

Nếu bật tính năng xác thực thì khi đăng nhập vào tài khoản sẽ bị hỏi thêm 1 bước nữa. Yêu cầu nhập mã OTP để xác thực là chủ tài khoản thì mới có thể vào tài khoản và sử dụng.

Demo

Chúng ta sẽ thêm xác thực hai lớp vào ứng dụng Rails bằng Nexmo Verify API. Tôi sẽ tạo một trang web nhỏ tên là "Kittens & Co" - mạng xã hội để trao đổi mèo.

Tải ứng dụng tại đây:

# ensure you have Ruby and Bundler installed
git clone https://github.com/nexmo-community/nexmo-rails-devise-2fa-demo.git
cd nexmo-rails-devise-2fa-demo
bundle install
rake db:migrate RAILS_ENV=development
rails server

Sau đó vào 127.0.0.1:3000 trên trình duyệt và đăng ký.

Mặc định, ứng dụng thực hiện đăng ký và đăng nhập bằng Devise, tôi sẽ tập trung vào phương thức xác thực thứ 2. Ngoài ra, để giao diện đẹp hơn tôi đã thêm gem bootstrap-sassdevise-bootstrap-templates.

Mã nguồn cho phần đăng nhập cơ bản ở trên nhánh basic-login. Phần xác thực thứ 2 ở nhánh two-factor. Để thuận tiện, bạn có thể theo dõi thay đổi trên Github.

Xác thực Nexmo cho 2FA

Nexmo Verify là phương thức xác thực qua điện thoại được thực hiện đơn giản. Hầu hết các plugin xác thực 2 lớp sẽ yêu cầu bạn quản lý các mã xác thực, thời gian hiệu lực và gửi SMS. Tuy nhiên, những gì bạn thực sự cần biết là gọi 2 API.

Thêm Nexmo Verify vào ứng dụng:

  • Thêm số điện thoại vào thông tin tài khoản.
  • Yêu cầu xác thực khi đăng nhập nếu có số điện thoại trong tài khoản.
  • Mã xác thực được gửi đến số điện thoại của người dùng và được dùng để đăng nhập.

Thêm số điện thoại

Thêm cột phone_number vào bảng User:

rails generate migration add_phone_number_to_users

# db/migrate/...add_phone_number_to_users.rb
class AddPhoneNumberToUsers < ActiveRecord::Migration
  def change
    add_column :users, :phone_number, :string
  end
end

rake db:migrate

Tạo bản sao của view bằng devise generate:

# this is not the default generator but the one needed for the devise-bootstrap-templates gem
rails generate devise:views:bootstrap_templates
 
# uncomment and run the command below if you did not use the devise-bootstrap-templates gem
# rails generate devise:views:templates

Lệnh trên sẽ sao chép nhiều view vào app/views/devise/. Nhưng chúng ta không dùng hết các view này mà chỉ giữ lại: registrations/edit.html.erb.

Thay đổi duy nhất trong view này là thêm trường phone_number ngay sau trường email.

<!-- app/views/devise/registrations/edit.html.erb -->
<div class="form-group">
  <%= f.label :phone_number %> <i>(Leave blank to disable two factor authentication)</i><br />
  <%= f.number_field :phone_number, class: "form-control", placeholder: "e.g. 447555555555 or 1234234234234"  %>
</div>

Bước cuối cùng là làm cho Devise nhận thức được tham số bổ sung này. Nếu không có những dòng này phone_number sẽ không được chấp nhận như là một tham số và sẽ bị mất sau khi form được gửi.

# app/controllers/application_controller.rb
...
before_filter :configure_permitted_parameters, if: :devise_controller?
 
def configure_permitted_parameters
  devise_parameter_sanitizer.permit(:account_update, keys: [:phone_number])
end
...

Để kiểm tra, đến http: //localhost: 3000/users/sign_up , tạo một tài khoản, nhấp vào email của bạn ở góc trên cùng bên phải của màn hình, nhập số điện thoại và mật khẩu bạn đã sử dụng khi đăng ký và nhấp vào Update. Số điện thoại sẽ được lưu vào cơ sở dữ liệu.

Gửi yêu cầu xác thực 2FA

Bây giờ, người dùng có thể thêm số điện thoại vào tài khoản của mình, có thể yêu cầu họ xác thực số điện thoại khi đăng nhập. Để gửi thông báo xác thực qua Nexmo Verify, thêm gem nexmo vào ứng dụng.

# Gemfile
gem 'nexmo'
gem 'dotenv-rails', groups: [:development, :test]

bundle install

Như bạn thấy, chúng ta cũng cần thêm gem dotenv-rails. Đây là ứng dụng có thể tải chứng chỉ API từ một tập tin .env. Gem Nexmo tự động lấy các biến môi trường đó và sử dụng chúng để khởi tạo client. Bạn có thể tìm thấy các thông tin xác thực của mình trên trang cài đặt tài khoản Nexmo của bạn.

# .env
NEXMO_API_KEY='your_key'
NEXMO_API_SECRET='your_secret'

Có nhiều cách khác nhau thêm bước xác thực thứ 2 vào ứng dụng. Để đơn giản, chúng ta thêm vào trong before_action của ApplicationController nếu người dùng kích hoạt xác thực 2 lớp, đảm bảo rằng chúng được xác thực trước khi được phép tiếp tục.

# app/controllers/application_controller.rb
before_action :verify_user!, unless: :devise_controller?
 
def verify_user!
  start_verification if requires_verification?
end

Đơn giản là kiểm tra nếu người dùng có số điện thoại trong hồ sơ và nếu giá trị session :verified chưa được đặt.

# app/controllers/application_controller.rb
def requires_verification?
  session[:verified].nil? && !current_user.phone_number.blank?
end

Để bắt đầu xác thực, gọi send_verification_request(API #1) trên đối tượng Nexmo::Client. Không cần phải qua thông tin xác thực API nào bởi vì nó đã được tạo thông qua các biến môi trường - nếu muốn kiểm tra có thể xem tại đây.

# app/controllers/application_controller.rb
def start_verification
  result = Nexmo::Client.new.send_verification_request(
    number: current_user.phone_number,
    brand: "Kittens and Co",
    sender_id: 'Kittens'
  )
  if result['status'] == '0'
    redirect_to edit_verification_path(id: result['request_id'])
  else
    sign_out current_user
    redirect_to :new_user_session, flash: {
      error: 'Could not verify your number. Please contact support.'
    }
  end
end

Như vậy là đã vượt qua yêu cầu xác thực tên của ứng dụng. Điều này được sử dụng trong tin nhắn văn bản mà người dùng nhận được và thêm vào một số cá nhân hóa thương hiệu rất tốt.

Nếu tin nhắn được gửi thành công, tôi sẽ chuyển hướng người dùng đến trang để điền mã xác nhận.

Kiểm tra mã xác thực 2FA

Bước cuối cùng là để xác nhận lại mã xác thực người dùng nhận được. Ta cần thêm một trang mới.

Bắt đầu bằng thêm trong routes.

# config/routes.rb
resources :verifications, only: [:edit, :update]

Và cũng tạo một controller.

# app/controllers/verifications_controller.rb
class VerificationsController < ApplicationController
  skip_before_action :verify_user!
 
  def edit
  end
 
  def update
    ...
  end
end

Để chắc chắn bỏ qua before_action ta đã thêm vào ApplicationController trước đó, ta dùng skip_before_action để trình duyệt không bị redirect vô hạn.

Tiếp theo, tạo view để người dùng nhập mã xác thực.

<!-- app/views/verifications/edit.html.erb -->
<%= form_tag verification_path(id: params[:id]), method: :put do %>
  <div class="form-group">
    <%= label_tag :code %><br />
    <%= number_field_tag :code, class: "form-control"  %>
  </div>
  <%= submit_tag 'Verify', class: "btn btn-primary" %>
<% end %>

Người dùng nhập mã và verify. Trong action này chúng ta lấy request_idcode từ params và dùng method check_verification_request(API #2) xác thực.

# app/controllers/verifications_controller.rb
def update
  confirmation = Nexmo::Client.new.check_verification_request(
    request_id: params[:id],
    code: params[:code]
  )
 
  if confirmation['status'] == '0'
    session[:verified] = true
    redirect_to :root, flash: { success: 'Welcome back.' }
  else
    redirect_to edit_verification_path(id: params[:id]), flash: { error: confirmation['error_text'] }
  end
end

Khi xác nhận thành công, chúng tôi sẽ thiết lập trạng thái của người dùng là đã được xác thực và chuyển hướng về trang chính. Nếu không sẽ thông báo những gì đã xảy ra. Tra cứu mã trạng thái phản hồi tại đây.

Nếu bạn đang chạy ứng dụng, đăng xuất (nếu cần) và đăng nhập, bây giờ bạn sẽ nhận được yêu cầu xác thực.

Như vậy, chỉ cần gọi 2 API.

Bước tiếp

Nexmo API có nhiều tùy chọn hơn những gì tôi đã đề cập ở đây, từ tìm kiếm và kiểm soát các yêu cầu, thay đổi độ dài mã và thời gian hết hạn. Ứng dụng trên mặc dù đơn giản nhưng đã được kiểm chứng là thực sự mạnh mẽ. Hệ thống sẽ chuyển sang các cuộc gọi thoại nếu cần thiết, xử lý hết hạn các token mà bạn không phải làm gì, ngăn ngừa sử dụng lại các mã xác thực.

Thư viện Nexmo của Ruby có thể làm được nhiều điều rất khác biệt so với tôi đã làm ở đây. Ví dụ: bạn có thể yêu cầu số điện thoại đăng ký từ người dùng, từ chối các tài khoản mới cho đến khi số điện thoại được xác nhận.

Nguồn: nexmo.com


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí