Resources

Summary

Copy https://github.com/travist/jsencrypt/blob/master/bin/jsencrypt.js to your vendor/assets/javascripts folder

# Terminal
openssl genrsa -out rsa_1024_priv.pem 1024
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem

You will want to avoid storing the public and private keys in application.rb. Instead, you should have them in your secrets.yml, environment variable, parameter store or elsewhere secure.

# application.rb
PUBLIC_KEY = <PUBLIC KEY DATA>
PRIVATE_KEY = <PUBLIC KEY DATA>

# application.js
//= require jsencrypt

# application.js
$(document).on('turbolinks:load', function(){
  $('form').submit(function( event ) {
    var encrypt = new JSEncrypt();
    $('[data-encrypt]').each(function(){
      unencrypted = $(this);
      encrypt.setKey($('#public_key').val());
      encrypted = encrypt.encrypt(unencrypted.val());
      if (encrypted != false) {
        unencrypted.val(encrypted);
      }  
    })
    // event.preventDefault();
  });
});

# users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]

  # POST /resource/sign_in
  def create
    # self.resource = warden.authenticate!(auth_options)
    resource = User.find_by(email: params[:user][:email])
    if resource&.valid_password?(authenticate_encryptor)
      set_flash_message!(:notice, :signed_in)
      sign_in(resource_name, resource)
      yield resource if block_given?
      respond_with resource, location: after_sign_in_path_for(resource)
    else
      redirect_to new_user_session_path, alert: 'Bad email or password.'
    end
  end

  private

  def authenticate_encryptor
    private_key = OpenSSL::PKey::RSA.new(PRIVATE_KEY)
    password = params[:user][:password]
    private_key.private_decrypt(Base64.decode64(password))
  end
end

# views/devise/sessions/new.html.erb
<%= f.password_field :password, autocomplete: "off", placeholder: 'Password', class: 'form-control', data: { encrypt: true } %>