Session Timeout Modal
Summary
At work we started enforcing users to be logged out after 30 minutes to improve security.
The way we were doing it lead to an issue with users being unable to complete forms due to getting logged out while going on lunch.
I needed a way of giving a user an easy way of logging back in after being logged out.
So I made an easy way for users to sign back in after getting automatically signed out using two gems Devise Gem, Auto Session Timeout and some custom code.

Devise Gem
I used devise as the back bone of my authentication.
You should be able follow those instructions to get devise setup and follow on for there.
If you didn’t use devise you will have changes to make in your routes and where certain methods are placed.
Auto Session Timeout Gem
I hoped to find a gem that would solve all my issues and I found an interesting Gem Auto Session Timeout but it only gave me some of the features I needed.
When setup it’ll allow you to automatically redirect a user to the login in page or other route after a user hasn’t sent an action to the server in 30 minutes.
Auto Session Timeout Setup
The main feature I wanted to be able to show a user a login modal automatically and not have to leave their current page or progress so I made some changes.
Follow the instructions from Auto Session Timeout for the devise setup.
Instead of the timeout route make a login_from_modal route. This will (You might have guessed it) allow us to login from the modal.
config\routes.rb
devise_scope :user do
post 'login_from_modal', to: "users/sessions#login_from_modal"
get "active", to: "users/sessions#active"
end
Another small change I made using Devise.timeout_in to set the auto_session_timeout.
app\controllers\application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!
auto_session_timeout Devise.timeout_in
end
The next change we want is the ability to show a modal instead of redirecting to the login page. To make these changes I pulled out the code they had in their helper method (auto_session_timeout_helper.rb) and made one change. I just wanted to be able to show a modal when the user was inactive for 30 minutes. Every 60 seconds the server is pinged to check if the user has completed any actions in the last 30 minutes. If they haven’t they will be logged out.
app\views\layouts\application.html.erb
To change the redirect to a modal show you’ll want to replace
<%= auto_session_timeout_js %>
with
<script>
function periodical_query() {
var request = new XMLHttpRequest();
request.onload = function (event) {
var status = event.target.status;
var response = event.target.response;
if (
status === 200 &&
(response === false || response === "false" || response === null)
) {
document.getElementById("login-modal").style.display = "block"; // This line was added to display the modal
}
};
request.open("GET", "/active", true);
request.responseType = "json";
request.send();
setTimeout(periodical_query, 60 * 1000); // Pings server every 60 seconds
}
setTimeout(periodical_query, 60 * 1000);
</script>
On the same page I then rendered the login modal.
<%= render 'layouts/login_modal'%>
Login Modal
I added in some code from w3schools just to build a quick modal.
app\views\layouts\application.html.erb
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css" />
app\views\layouts_login_modal.html.erb
For my modal I created a form with two fields. One for the users email and another for their password. The user is only shown one field for their password. ( As they will want to log back into the same account ) I’m using the devise method current_user.email to get the users email who is currently logged in and setting that to a hidden field.
<div id="login-modal" class="w3-modal">
<div class="w3-modal-content">
<div class="w3-container">
<div class="col-10 mx-auto">
<div class="col-12">
<p class="text-center my-3" style="font-size: 1rem;">
You have been logged out due to inactivity. Please enter your
password to log in.
</p>
</div>
<%### Flash %>
<div
id="login-modal-flash-message"
class="pt-3 mb-2"
style="display: none;"
>
<p>Incorrect Password</p>
</div>
<%### Hidden Email Address %> <%= hidden_field_tag "login-modal-email",
"#{current_user&.email}" %> <%### Password %>
<div class="col-12 form-group">
<label for="password" class="form-label">Password</label>
<%= password_field_tag "password", "", class: "form-control",
id:"login-modal-password", placeholder: "Password", required: true %>
</div>
<%### Submit Button %>
<div class="actions text-center m-t-20 m-b-20 col-12">
<%= submit_tag "Log In", id:"login-modal-submit-button", class:"btn
btn-primary font-16 w-100", onclick: "login_modal_submit(this);" %>
</div>
</div>
</div>
</div>
</div>

Login Modal - Javascript
Pressing the submit button causes a javascript function to be called to handle the event. The users password and email are grabbed from the forum and sent to the sessions controller. On success the modal is hidden. On failure message is shown that the password is incorrect.
var login_modal_submit = function (element) {
var password = document.getElementById("login-modal-password").value;
var email = document.getElementById("login-modal-email").value;
var request = new XMLHttpRequest();
request.onload = function (event) {
var status = event.target.status;
if (status === 200) {
document.getElementById("login-modal").style.display = "none";
} else {
document.getElementById("login-modal-flash-message").style.display =
"block";
setTimeout(() => {
var login_message = document.getElementById(
"login-modal-flash-message"
);
login_message.style.display = "none";
}, 1000);
}
};
request.open("POST", "<%= create_from_ajax_path %>", true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.send("email=" + email + "&password=" + password + "");
};
Login Modal - Controller
In my sessiosn controller I then added a login_from_modal method to handle checking if the user details passed were valid. If would then return a success or failure message.
app\controllers\users\sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
auto_session_timeout_actions
## Skip authentication as the user is logged out for these actions
skip_before_action :authenticate_user!, only: [:login_from_modal], :raise => false
skip_before_action :verify_authenticity_token, only: [:login_from_modal]
# Checks if the user details are valid
def login_from_modal
resource = User.find_for_database_authentication(email: params[:email])
return invalid_login_attempt unless resource
if resource.valid_password?(params[:password])
sign_in :user, resource
render json: { message: "success" }, :status => 200
else
invalid_login_attempt
end
end
def active
render_session_status
end
private
def invalid_login_attempt
render json: { message: "Invalid Email or Password" }, :status => 401
end
end
Hopefully at the end of all that you have a nice automatic modal to login with.
Road Ahead
Over the next week I plan to:
Look at adding Action Cable to my project to so that successfully logging in on one modal will close all login modals for that user.
Thanks for reading.