├── .github └── workflows │ └── minitest.yml ├── .gitignore ├── CHANGELOG ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── auto-session-timeout.gemspec ├── lib ├── auto-session-timeout.rb ├── auto │ └── session │ │ ├── timeout.rb │ │ └── timeout │ │ └── version.rb ├── auto_session_timeout.rb └── auto_session_timeout_helper.rb └── test ├── auto_session_timeout_helper_test.rb ├── auto_session_timeout_test.rb └── test_helper.rb /.github/workflows/minitest.yml: -------------------------------------------------------------------------------- 1 | name: Minitest 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | rails-version: ['3.2', '4.0', '4.1', '4.2', '5.0', '5.1', '5.2', '6.0', '6.1', '7.0.1', '7.1', '7.2'] 16 | ruby-version: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2'] 17 | exclude: 18 | - rails-version: '3.2' 19 | ruby-version: '2.7' 20 | - rails-version: '3.2' 21 | ruby-version: '3.0' 22 | - rails-version: '3.2' 23 | ruby-version: '3.1' 24 | - rails-version: '3.2' 25 | ruby-version: '3.2' 26 | 27 | - rails-version: '4.0' 28 | ruby-version: '2.7' 29 | - rails-version: '4.0' 30 | ruby-version: '3.0' 31 | - rails-version: '4.0' 32 | ruby-version: '3.1' 33 | - rails-version: '4.0' 34 | ruby-version: '3.2' 35 | 36 | - rails-version: '4.1' 37 | ruby-version: '2.7' 38 | - rails-version: '4.1' 39 | ruby-version: '3.0' 40 | - rails-version: '4.1' 41 | ruby-version: '3.1' 42 | - rails-version: '4.1' 43 | ruby-version: '3.2' 44 | 45 | - rails-version: '4.2' 46 | ruby-version: '2.4' 47 | - rails-version: '4.2' 48 | ruby-version: '2.7' 49 | - rails-version: '4.2' 50 | ruby-version: '3.0' 51 | - rails-version: '4.2' 52 | ruby-version: '3.1' 53 | - rails-version: '4.2' 54 | ruby-version: '3.2' 55 | 56 | - rails-version: '5.0' 57 | ruby-version: '2.4' 58 | - rails-version: '5.0' 59 | ruby-version: '3.0' 60 | - rails-version: '5.0' 61 | ruby-version: '3.1' 62 | - rails-version: '5.0' 63 | ruby-version: '3.2' 64 | 65 | - rails-version: '5.1' 66 | ruby-version: '2.4' 67 | - rails-version: '5.1' 68 | ruby-version: '3.0' 69 | - rails-version: '5.1' 70 | ruby-version: '3.1' 71 | - rails-version: '5.1' 72 | ruby-version: '3.2' 73 | 74 | - rails-version: '5.2' 75 | ruby-version: '2.4' 76 | - rails-version: '5.2' 77 | ruby-version: '3.0' 78 | - rails-version: '5.2' 79 | ruby-version: '3.1' 80 | - rails-version: '5.2' 81 | ruby-version: '3.2' 82 | 83 | - rails-version: '6.0' 84 | ruby-version: '2.4' 85 | 86 | - rails-version: '6.1' 87 | ruby-version: '2.4' 88 | 89 | - rails-version: '7.0.1' 90 | ruby-version: '2.4' 91 | - rails-version: '7.0.1' 92 | ruby-version: '2.5' 93 | - rails-version: '7.0.1' 94 | ruby-version: '2.6' 95 | 96 | - rails-version: '7.1' 97 | ruby-version: '2.4' 98 | - rails-version: '7.1' 99 | ruby-version: '2.5' 100 | - rails-version: '7.1' 101 | ruby-version: '2.6' 102 | 103 | - rails-version: '7.2' 104 | ruby-version: '2.4' 105 | - rails-version: '7.2' 106 | ruby-version: '2.5' 107 | - rails-version: '7.2' 108 | ruby-version: '2.6' 109 | - rails-version: '7.2' 110 | ruby-version: '2.7' 111 | - rails-version: '7.2' 112 | ruby-version: '3.0' 113 | steps: 114 | - uses: actions/checkout@v2 115 | - name: Set up Ruby 116 | uses: ruby/setup-ruby@v1 117 | env: 118 | RAILS_VERSION: ${{ matrix.rails-version }} 119 | with: 120 | ruby-version: ${{ matrix.ruby-version }} 121 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 122 | - name: Run tests 123 | run: bundle exec rake 124 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | *.sublime-project 7 | *.sublime-workspace 8 | .idea 9 | Gemfile.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2023-10-24 - v1.1 2 | 3 | 2023-10-09 - Compatibility with Rails 7.1 [tim-s-ccs] 4 | 5 | 2023-01-18 - v1.0 6 | 7 | 2023-01-18 - Compatablity issue with Rack 2.2.4 [GearoidDC] 8 | 2022-01-20 - Use GitHub Actions build badge [pelargir] 9 | 10 | 2022-01-04 - v0.9.9 11 | 12 | 2022-01-04 - Rails 7 compatibility [pelargir] 13 | 14 | 2021-10-11 - v0.9.8 15 | 16 | 2021-10-10 - Custom session timeout method [JunaidUK] 17 | 18 | 2021-02-02 - v0.9.7 19 | 20 | 2021-02-02 - Rails 6 compatibility [jasonheecs] 21 | 22 | 2019-10-15 - v0.9.6 23 | 24 | 2019-10-15 - Use routes in JS helper [pelargir] 25 | 26 | 2019-03-03 - Update README [cprodhomme] 27 | 28 | 2018-12-21 - Support Rails protect_from_forgery [davegudge] 29 | 30 | 2017-06-13 - v0.9.5 31 | 32 | 2017-06-12 - Exclude controller actions from CSRF verification [pelargir] 33 | 34 | 2017-06-12 - Make updater use vanilla JS [emilos] 35 | 36 | 2017-05-16 - v0.9.4 37 | 38 | 2017-05-08 - Rails 5 compatibility [quainjn] 39 | 40 | 2016-10-14 - Allow defining verbosity [zaimramlan] 41 | 42 | 2013-08-29 - v0.9.3 43 | 44 | 2013-08-28 - Add jQuery support [krishnasrihari] 45 | 46 | 2013-07-24 - v0.9.2 47 | 48 | 2013-07-24 - Add tests and use Ruby 1.9 hash syntax [pelargir] 49 | 50 | 2013-07-24 - v0.9.1 51 | 52 | 2013-07-24 - Timeout can be set in controller or user model [pelargir] 53 | 54 | 2013-07-22 - v0.9 55 | 56 | 2013-07-21 - Support for jQuery periodical updater plugin [pelargir] 57 | 58 | 2013-07-17 - v0.8 59 | 60 | 2014-07-14 - Added jQuery support [krishnasrihari] 61 | 62 | 2013-06-23 - v0.7 63 | 64 | 2013-06-22 - Switched to Bundler for generating the gemspec [pelargir] 65 | 66 | 2009-08-22 - v0.5 67 | 68 | 2009-06-03 - Move controller actions into plugin [pelargir] 69 | 70 | 2009-04-22 - Add JS helper [pelargir] 71 | 72 | 2009-04-22 - Initial import [pelargir] 73 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | rails_version = ENV['RAILS_VERSION'] 4 | gem 'actionpack', rails_version 5 | 6 | # Specify your gem's dependencies in auto-session-timeout.gemspec 7 | gemspec 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Matthew Bass (http://www.matthewbass.com) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Auto::Session::Timeout 4 | 5 | Provides automatic session timeout in a Rails application. Very easy 6 | to install and configure. Have you ever wanted to force your users 7 | off your app if they go idle for a certain period of time? Many 8 | online banking sites use this technique. If your app is used on any 9 | kind of public computer system, this gem is a necessity. 10 | 11 | ## Installation 12 | 13 | Add this line to your application's Gemfile: 14 | 15 | ```ruby 16 | gem 'auto-session-timeout' 17 | ``` 18 | 19 | And then execute: 20 | 21 | ``` 22 | $ bundle 23 | ``` 24 | 25 | Or install it yourself as: 26 | 27 | ``` 28 | $ gem install auto-session-timeout 29 | ``` 30 | 31 | ## Usage 32 | 33 | After installing, tell your application controller to use auto timeout: 34 | 35 | ```ruby 36 | class ApplicationController < ActionController::Base 37 | auto_session_timeout 1.hour 38 | end 39 | ``` 40 | 41 | This will use a global timeout of 1 hour. The gem assumes your authentication 42 | provider has a `#current_user` method that returns the currently logged in user. 43 | 44 | If you want to specify a custom timeout value per user, don't pass a value to 45 | the controller as shown above. Instead, override `#auto_timeout` in your 46 | `#current_user` model. This is typically the `User` class: 47 | 48 | ```ruby 49 | class ApplicationController < ActionController::Base 50 | auto_session_timeout 51 | end 52 | 53 | class User < ActiveRecord::Base 54 | def auto_timeout 55 | 15.minutes 56 | end 57 | end 58 | ``` 59 | 60 | You will also need to insert a call to the `#auto_session_timeout_js` 61 | helper method inside the body tags in your views. The easiest way to 62 | do this is to insert it once inside your default or application-wide 63 | layout. Make sure you are only rendering if the user is logged in, 64 | otherwise the gem will attempt to force non-existent sessions to 65 | timeout, wreaking havoc: 66 | 67 | ```erb 68 |
69 | <% if current_user %> 70 | <%= auto_session_timeout_js %> 71 | <% end %> 72 | 73 | ``` 74 | 75 | You need to setup two actions: one to return the session status and 76 | another that runs when the session times out. You can use the default 77 | actions included with the gem by inserting this line in your target 78 | controller (most likely your user or session controller): 79 | 80 | ```ruby 81 | class SessionsController < ApplicationController 82 | auto_session_timeout_actions 83 | end 84 | ``` 85 | 86 | To customize the default actions, simply override them. You can call 87 | the `#render_session_status` and `#render_session_timeout` methods to 88 | use the default implementation from the gem, or you can define the 89 | actions entirely with your own custom code: 90 | 91 | ```ruby 92 | class SessionsController < ApplicationController 93 | def active 94 | render_session_status 95 | end 96 | 97 | def timeout 98 | render_session_timeout 99 | end 100 | end 101 | ``` 102 | 103 | In any of these cases, make sure to properly map the actions in your 104 | routes.rb file: 105 | 106 | ```ruby 107 | get "active", to: "sessions#active" 108 | get "timeout", to: "sessions#timeout" 109 | ``` 110 | 111 | You're done! Enjoy watching your sessions automatically timeout. 112 | 113 | ## Using with Devise 114 | 115 | When using Devise for authentication you will need to add a scoped 116 | sessions controller and call the timeout actions helper there. 117 | For example: 118 | 119 | ```ruby 120 | class Users::SessionsController < Devise::SessionsController 121 | auto_session_timeout_actions 122 | end 123 | ``` 124 | 125 | In your routes.rb file you will need to declare your scoped controller 126 | and declare the timeout actions inside the same Devise scope: 127 | 128 | ```ruby 129 | Rails.application.routes.draw do 130 | devise_for :users, controllers: { sessions: "users/sessions" } 131 | 132 | devise_scope :user do 133 | get "active", to: "users/sessions#active" 134 | get "timeout", to: "users/sessions#timeout" 135 | end 136 | end 137 | ``` 138 | 139 | You can use Devise's `#user_signed_in?` method when you call the JS helper 140 | method in your view: 141 | 142 | ```erb 143 | 144 | <% if user_signed_in? %> 145 | <%= auto_session_timeout_js %> 146 | <% end %> 147 | 148 | ``` 149 | 150 | ## Optional Configuration 151 | 152 | By default, the JavaScript code checks the server every 60 seconds for 153 | active sessions. If you prefer that it check more frequently, pass a 154 | frequency attribute to the helper method. The frequency is given in 155 | seconds. The following example checks the server every 15 seconds: 156 | 157 | ```erb 158 | 159 | <% if current_user %> 160 | <%= auto_session_timeout_js frequency: 15 %> 161 | <% end %> 162 | 163 | ``` 164 | 165 | ## Contributing 166 | 167 | 1. Fork it 168 | 2. Create your feature branch (`git checkout -b my-new-feature`) 169 | 3. Commit your changes (`git commit -am 'Add some feature'`) 170 | 4. Push to the branch (`git push origin my-new-feature`) 171 | 5. Create new Pull Request 172 | 173 | ## Resources 174 | 175 | * Repository: http://github.com/pelargir/auto-session-timeout/ 176 | * Blog: http://www.matthewbass.com 177 | * Author: Matthew Bass 178 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rdoc/task' 4 | require 'bundler/gem_tasks' 5 | 6 | task default: :test 7 | 8 | Rake::TestTask.new('test') do |t| 9 | t.pattern = 'test/*_test.rb' 10 | end 11 | -------------------------------------------------------------------------------- /auto-session-timeout.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'auto/session/timeout/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "auto-session-timeout" 8 | spec.version = Auto::Session::Timeout::VERSION 9 | spec.authors = ["Matthew Bass"] 10 | spec.email = ["pelargir@gmail.com"] 11 | spec.description = %q{Automatic session timeout in Rails} 12 | spec.summary = %q{Provides automatic session timeout in a Rails application.} 13 | spec.homepage = "http://github.com/pelargir/auto-session-timeout" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency "actionpack", [">= 3.2", "< 7.3"] 22 | spec.add_development_dependency "bundler", "~> 2.0" 23 | spec.add_development_dependency "rake", "~> 13.0" 24 | spec.add_development_dependency "minitest", [">= 4.2", "< 6"] 25 | end 26 | -------------------------------------------------------------------------------- /lib/auto-session-timeout.rb: -------------------------------------------------------------------------------- 1 | require 'auto_session_timeout' 2 | require 'auto_session_timeout_helper' -------------------------------------------------------------------------------- /lib/auto/session/timeout.rb: -------------------------------------------------------------------------------- 1 | require "auto/session/timeout/version" 2 | 3 | module Auto 4 | module Session 5 | module Timeout 6 | # Your code goes here... 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/auto/session/timeout/version.rb: -------------------------------------------------------------------------------- 1 | module Auto 2 | module Session 3 | module Timeout 4 | VERSION = "1.2" 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/auto_session_timeout.rb: -------------------------------------------------------------------------------- 1 | module AutoSessionTimeout 2 | 3 | def self.included(controller) 4 | controller.extend ClassMethods 5 | end 6 | 7 | module ClassMethods 8 | def auto_session_timeout(seconds=nil) 9 | prepend_before_action do |c| 10 | if session_expired?(c) && !signing_in?(c) 11 | handle_session_reset(c) 12 | else 13 | unless c.request.original_url.start_with?(c.send(:active_url)) 14 | offset = seconds || (current_user.respond_to?(:auto_timeout) ? current_user.auto_timeout : nil) 15 | c.session[:auto_session_expires_at] = Time.now + offset if offset && offset > 0 16 | end 17 | end 18 | end 19 | end 20 | 21 | def auto_session_timeout_actions 22 | define_method(:active) { render_session_status } 23 | define_method(:timeout) { render_session_timeout } 24 | end 25 | end 26 | 27 | def render_session_status 28 | response.headers["Etag"] = nil # clear etags to prevent caching 29 | render plain: !!current_user, status: 200 30 | end 31 | 32 | def render_session_timeout 33 | flash[:notice] = t("devise.failure.timeout", default: "Your session has timed out.") 34 | redirect_to sign_in_path 35 | end 36 | 37 | private 38 | 39 | def handle_session_reset(c) 40 | c.send :reset_session 41 | end 42 | 43 | def signing_in?(c) 44 | c.request.env["PATH_INFO"] == sign_in_path && c.request.env["REQUEST_METHOD"] == "POST" 45 | end 46 | 47 | def session_expired?(c) 48 | c.session[:auto_session_expires_at].try(:<, Time.now) 49 | end 50 | 51 | def sign_in_path 52 | user_session_path 53 | rescue 54 | "/login" 55 | end 56 | 57 | end 58 | 59 | ActionController::Base.send :include, AutoSessionTimeout 60 | -------------------------------------------------------------------------------- /lib/auto_session_timeout_helper.rb: -------------------------------------------------------------------------------- 1 | module AutoSessionTimeoutHelper 2 | def auto_session_timeout_js(options={}) 3 | frequency = options[:frequency] || 60 4 | attributes = options[:attributes] || {} 5 | code = <