- vừa được xem lúc

Xác thực 2 bước với google authenticator chưa bao giờ dễ dàng đến thế - P2

0 0 14

Người đăng: Le Duc Son

Theo Viblo Asia

Chào các bạn. Ở phần 1 mình có giới thiệu về xác thực 2 bước đối với google authenticator cơ bản. Ở phần 2 này mình sẽ giới thiệu với các bạn làm config code làm sao để có thể xác thực bước thứ 2 bằng mã otp từ google authenticator

Ở phần trước chúng ta đã tạo được mã QR và thực hiện verify rồi, bây giờ chúng ta tiếp tục tạo mã backup codes sau khi verify

Backup Codes

Chúng ta tạo ra 10 mã backup codes, dùng trong trường hợp người dùng mất điện thoại, hoặc lỡ tay xóa ứng dụng google authenticator

Mã backup code được tạo ra như thế nào

backup_codes = current_user.generate_otp_backup_codes!
current_user.save!

Câu lệnh trên sẽ tạo mã backup codes và thực hiện lưu vào database của mình theo kiểu array. Mỗi khi userdùng 1 mã backup codes để thực hiện verify thì trong database sẽ tự động remove đi 1 mã. Cho nên với 10 mã, chúng ta được sử dụng 10 lần thay mã otp google authenticator 10 lần.

Sau khi thực hiên scan và verify otp, thì chúng ta sẽ chuyển qua trang backup codes này. Để thuận tiện cho hiển thị và sử dụng chức năng download, thì chúng ta nên lưu mã backup code này vào session để còn sử dụng lại. Nguyên nhân, vì mã backup code nếu đã được lưu vào database rồi, thì nó sẽ tiến hành mã hóa luôn, và không thể giải mã được. Nên cần lưu vào session để tiện sử dụng ở những chỗ khác. Khi enable chức năng two factor thành công thì chúng ta sẽ xóa session này đi

Download and Copy backup codes

Download

Hành động này sẽ tải về 1 file txt chưa mã backup code

def download send_data params[:two_fa][:codes], filename: "backup_codes.txt"
end
<%= form_tag(download_two_factor_settings_path, method: :post, class: "form-horizontal form-label-left download-two-factor") do %> <textarea name="two_fa[codes]" class="list-backup-codes hide"></textarea> <button type="button" class="btn btn-default btn-two-factor-download"> <i class="fa fa-download"></i> Download </button> <button type="button" class="btn btn-default btn-two-factor-copy"> <i class="fa fa-clipboard"></i> Copy </button> <% end %>

Code javascript khi thực hiện click vào button download

 $(document).on('click', '.btn-two-factor-download', function() { $('.list-backup-codes').val(getOtpBackupCodes()); $('.download-two-factor').submit(); $('.btn-enable-two-factor').removeAttr('disabled'); });

Copy

$(document).on('click', '.btn-two-factor-copy', function() { $('.list-backup-codes').val(getOtpBackupCodes()); $('.list-backup-codes').removeClass('hide'); $('.list-backup-codes').select(); document.execCommand('copy'); $('.list-backup-codes').addClass('hide'); $('.copy_message').removeClass('hide'); $('.btn-enable-two-factor').removeAttr('disabled');
});

Chỉ đơn là chạy command thực hiện việc copy đống backup codes thôi. Rồi có thể paste vào bất cứ đâu để lưu lại

Disable two factor setting

Sau khi config two factor thành công, thì còn lại là disable chức năng two factor setting, và 1 chức năng nữa đó là tạo lại mã backup codes mới

Chức năng tạo mã backup code mới cho những user đã enable two factor, về cơ bản là giống với việc tạo mã backup code khi bạn thực hiện scan qrcode

Require password

Để thực hiện việc bảo mật, mỗi khi user truy cập vào trang setting two factor này thì chúng ta sẽ yêu cầu họ nhập mật khẩu hiện tại của user. Nhập đúng thì mới chuyển hướng đến trang setting two factor setting.

Tạo function require password

def required_password return if session[:password_token] == current_user.encrypted_password render "two_factor_settings/required_password" end

Hàm này chỉ đơn giản là hiển thị form nhập mật khẩu thôi

before_action :required_password, only: [:new, :edit]

Chúng ta sẽ cho nó hiển thị form nhập password trước khi chuyển đến trang scan qrcode hoặc trang dissable two factor

def confirm_password unless current_user.valid_password? enable_2fa_params_password[:password] flash.now[:danger] = "Current password not valid. Please try again" return render "two_factor_settings/required_password" end session[:password_token] = current_user.encrypted_password redirect_to new_two_factor_settings_path end

Hàm này thực hiện kiểm tra mật khẩu nhập vào có đúng không và chuyển hướng đến trang config two factor

Mục đích để tránh có người không phải bản thân user thực hiện action. Hạn chế rủi ro về bảo mật tài khoản cho user

Nhập OTP, backup code sau khi login

Để có thể kiểm tra dược otp, backup code người dùng đúng hay không, chúng ta tạo ra module AuthenticateWithOtpTwoFactor để tiến hành xác thực

module AuthenticateWithOtpTwoFactor extend ActiveSupport::Concern def authenticate_with_otp_two_factor user = self.resource = find_user return prompt_for_two_factor(user) if session[:otp_user_id].blank? authenticate_user_with_backup_code_two_factor(user) if params[:code_type] == "backup_code" authenticate_user_with_otp_two_factor(user) unless params[:code_type] == "backup_code" end private def prompt_for_two_factor user return unless session[:otp_user_id] || user&.valid_password?(user_params[:password]) @user = user session[:otp_user_id] = user.id render "devise/sessions/two_factor" end def authenticate_user_with_otp_two_factor user if user_params[:otp_attempt].blank? flash.now[:danger] = "OTP Code not blank" prompt_for_two_factor user return end if user.current_otp == user_params[:otp_attempt] && user&.validate_and_consume_otp!(user_params[:otp_attempt]) session.delete :otp_user_id sign_in user else flash.now[:danger] = "OTP Code invalid. Please try again" prompt_for_two_factor user end rescue StandardError => e flash.now[:danger] = e.message prompt_for_two_factor user end def authenticate_user_with_backup_code_two_factor user if user_params[:backup_code_attempt].blank? flash.now[:danger] = "Backup Code not blank. Please try again" prompt_for_two_factor user return end if user&.invalidate_otp_backup_code!(user_params[:backup_code_attempt]) session.delete :otp_user_id sign_in user else flash.now[:danger] = "Backup code invalid" prompt_for_two_factor user end rescue StandardError => e flash.now[:danger] = e.message prompt_for_two_factor admin end def user_params params.require(:user).permit(:email, :password, :otp_attempt, :backup_code_attempt) end def find_user if user_params[:email] User.find_by email: user_params[:email] elsif session[:otp_user_id] User.find_by id: session[:otp_user_id] end end def otp_two_factor_enabled? find_user&.otp_required_for_login? end
end

Để nó có thể chạy vào module check, thì chúng ta phải làm cho trước khi vào vào session create của devise thì chúng ta sẽ gọi đến hàm check trong module

include AuthenticateWithOtpTwoFactor prepend_before_action :authenticate_with_otp_two_factor, if: -> {action_name == "create" && otp_two_factor_enabled?}

Mã HTML dùng để nhập OTP và password

<div class="container jumbotron"> <body class="login"> <div> <div class="login_wrapper"> <div class="animate form login_form"> <section class="login_content"> <% flash.each do |key, value| %> <% unless value.is_a? Hash%> <div class="alert alert-<%= key == "notice" ? "info" : "danger"%>"> <span class="close" data-dismiss="alert" aria-label="close">&times;</span> <%= value %> </div> <% end %> <% end %> <%= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| %> <div class="otp_code <%= "hide" if params[:code_type] == "backup_code" %>"> <div> <%= f.text_field :otp_attempt, class: "form-control", placeholder: "Enter OTP Code", autofocus: "autofocus" %> </div> <div class="form-group"> <span class="recover_code_link">Switch to backup code</span> </div> </div> <div class="backup_code <%= "hide" if params[:code_type] != "backup_code" %>"> <div> <%= f.text_field :backup_code_attempt, class: "form-control", placeholder: "Enter backup code", autofocus: "autofocus" %> </div> <div class="form-group"> <span class="otp_code_link">Switch to otp code</span> </div> </div> <div class="form-group"> <input type="hidden" name="code_type" id="admin_code_type" value="<%= params[:code_type]%>" /> <%= f.button :submit, class: "btn btn-default submit" do %> <span class="text">Confirm</span> <% end %> </div> <div class="clearfix"></div> <% end %> </section> </div> </div> </div> </body>
</div>

Kết

Đến đây chúng ta đã hoàn thành chức năng xác thực 2 bước sử dụng google authenticator rồi.

Source code hoàn chỉnh các bạn có thể xem tại đây

Source này mình có up lên heroku, muốn trải nghiệm thử các bạn vào đây để đăng ký 1 tài khoản. Sau đó login để trải nghiệm nhé.

Cảm ơn các bạn đã theo dõi.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tôi cá là bạn không biết những điều này - Ruby on rails ( Phần 2)

Các bạn có thể theo dõi phần 1 ở đây :. https://viblo.asia/p/toi-ca-la-ban-khong-biet-nhung-dieu-nay-ruby-on-rails-phan-1-WAyK8DDeKxX. 5.

0 0 222

- vừa được xem lúc

Rails Memoization

Memoization is a process that can be used to speed up rails methods. It caches the results of methods that do time-consuming work, work that only needs to be done once. Here is an example. Example.

0 0 48

- vừa được xem lúc

Tại sao Rails lại dùng cả Webpack lẫn Sprocket?

Khi Rails 6 được ra mắt, có thể bạn đã từng tự hỏi. WTF, sao Webpack đã được add vào rồi, mà Sprocket vẫn tồn tại thế kia . Chẳng phải Webpack và Sprocket được dùng để giải quyết chung một công việc hay sao. Hoặc cả đây:.

0 0 59

- vừa được xem lúc

Bạn nên sử dụng Elasticsearch trong ứng dụng Ruby On Rails như thế nào?

Elasticsearch là một công cụ phân tích và mã nguồn mở mạnh mẽ và linh hoạt, phân tán, theo thời gian thực. Đó là tiêu chuẩn vàng trong công nghệ tìm kiếm.

0 0 80

- vừa được xem lúc

Form object pattern trong rails

1.Mở đầu.

0 0 111

- vừa được xem lúc

Sử dụng Twilio để gửi SMS trong ứng dụng Ruby on Rails

Ngoài cách xác nhận tài khoản hay gửi thông báo bằng email thì hôm nay mình sẽ hướng dẫn các bạn 1 cách nữa là thông qua SMS/Voice. Công cụ sử dụng sẽ là gem Twilio. Installation. Để cài đặt bằng Bundler, hãy lấy phiên bản mới nhất:.

0 0 67