Skip to content

Commit c6b8d66

Browse files
author
Adam Cooke
committed
initial
0 parents  commit c6b8d66

12 files changed

+428
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.gem
2+

MIT-LICENSE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright 2014 Adam Cooke.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Authie
2+
3+
This is a Rails library which provides applications with a database-backed user
4+
sessions. This ensures that user sessions can be invalidated from the server and
5+
users activity can be easily tracked.
6+
7+
The "traditional" way of simply setting a user ID in your session is insecure
8+
and unwise. If you simply do something like the example below, it means that anyone
9+
with access to the session cookie can login as the user whenever and wherever they wish.
10+
11+
```ruby
12+
if user = User.authenticate(params[:username], params[:password])
13+
# Don't do this...
14+
session[:user_id] = user.id
15+
redirect_to root_path, :notice => "Logged in successfully!"
16+
end
17+
```
18+
19+
The design goals behind Authie are:
20+
21+
* Any session can be invalidated instantly from the server without needing to make
22+
changes to remote cookies.
23+
* We can see who is logged in to our application at any point in time.
24+
* Sessions should automatically expire after a certain period of inactivity.
25+
* Sessions can be either permanent or temporary.
26+
27+
## Installation
28+
29+
As usual, just pop this in your Gemfile:
30+
31+
```ruby
32+
gem 'authie', '~> 1.0.0'
33+
```
34+
35+
You will then need to run your `db:migrate` task to add the Authie sessions table
36+
to your local database.
37+
38+
```
39+
rake db:migrate
40+
```
41+
42+
## Usage
43+
44+
Authie is just a session manager and doesn't provide any functionality for your authentication or User models. Your `User` model should implement any methods needed to authenticate a username & password.
45+
46+
### Creating a new session
47+
48+
When a user has been authenticated, you can simply set `current_user` to the user
49+
you wish to login. You may have a method like this in a controller.
50+
51+
```ruby
52+
class AuthenticationController < ApplicationController
53+
54+
skip_before_filter: login_required
55+
56+
def login
57+
if request.post?
58+
if user = User.authenticate(params[:username], params[:password])
59+
self.current_user = user
60+
redirect_to root_path
61+
else
62+
flash.now[:alert] = "Username/password was invalid"
63+
end
64+
end
65+
end
66+
67+
end
68+
```
69+
70+
### Checking whether user's are logged in
71+
72+
On any subsequent request, you should make sure that your user is logged in.
73+
You may wish to implement a `login_required` controller method which is called
74+
before every action in your application.
75+
76+
```ruby
77+
class ApplicationController < ActionController::Base
78+
79+
before_filter :login_required
80+
81+
private
82+
83+
def login_required
84+
unless logged_in?
85+
redirect_to login_path, :alert => "You must login to view this resource"
86+
end
87+
end
88+
89+
end
90+
```
91+
92+
### Accessing the current user (and session)
93+
94+
There are a few controller methods which you can call which will return information about the current session:
95+
96+
* `current_user` - returns the currently logged in user model
97+
* `auth_session` - returns the current auth session
98+
* `logged_in?` - returns a true if there's a session or false if no user is logged in
99+
100+
### Logging out
101+
102+
In order to invalidate a session you can simply invalidate it.
103+
104+
```
105+
def logout
106+
auth_session.invalidate!
107+
redirect_to login_path, :notice => "Logged out successfully."
108+
end
109+
```
110+
111+
### Persisting sessions
112+
113+
In some cases, you may wish users to have a permanent sessions. In this case, you should ask users after they have logged in if they wish to "persist" their session across browser restarts. If they do wish to do this, just do something like this:
114+
115+
```ruby
116+
def persist_session
117+
auth_session.persist!
118+
redirect_to root_path, :notice => "You will now be remembered!"
119+
end
120+
```
121+
122+
### Accessing all user sessions
123+
124+
If you want to provide users with a list of their sessions, you can access all active sessions for a user. The best way to do this will be to add a `has_many` association to your User model.
125+
126+
```ruby
127+
class User < ActiveRecord::Base
128+
has_many :sessions, :class_name => 'Authie::Session', :foriegn_key => 'user_id', :dependent => :destroy
129+
end
130+
```

authie.gemspec

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require File.expand_path('../lib/authie/version', __FILE__)
2+
3+
Gem::Specification.new do |s|
4+
s.name = "authie"
5+
s.description = %q{A Rails library for storing user sessions in a backend database}
6+
s.summary = s.description
7+
s.homepage = "https://github.com/adamcooke/authie"
8+
s.licenses = ['MIT']
9+
s.version = Authie::VERSION
10+
s.files = Dir.glob("{lib,db}/**/*")
11+
s.require_paths = ["lib"]
12+
s.authors = ["Adam Cooke"]
13+
s.email = ["[email protected]"]
14+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class CreateAuthieSessions < ActiveRecord::Migration
2+
def change
3+
create_table :authie_sessions do |t|
4+
t.string :token, :browser_id
5+
t.integer :user_id
6+
t.boolean :active, :default => true
7+
t.text :data
8+
t.datetime :expires_at
9+
t.datetime :login_at
10+
t.string :login_ip
11+
t.datetime :last_activity_at
12+
t.string :last_activity_ip, :last_activity_path
13+
t.string :user_agent
14+
t.timestamps
15+
end
16+
end
17+
end

lib/authie.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
require 'authie/version'
2+
require 'authie/engine'
3+
require 'authie/config'
4+
require 'authie/error'

lib/authie/config.rb

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module Authie
2+
class Config
3+
4+
def session_inactivity_timeout
5+
@session_inactivity_timeout || 12.hours
6+
end
7+
attr_writer :session_inactivity_timeout
8+
9+
def persistent_session_length
10+
@persistent_session_length || 2.months
11+
end
12+
attr_writer :persistent_session_length
13+
14+
def user_model_class_name
15+
@user_model_class_name || 'User'
16+
end
17+
attr_writer :user_model_class_name
18+
19+
end
20+
21+
def self.config
22+
@config ||= Config.new
23+
end
24+
end

lib/authie/controller_extension.rb

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module Authie
2+
module ControllerExtension
3+
4+
def self.included(base)
5+
base.helper_method :logged_in?, :current_user, :auth_session
6+
base.before_filter :set_browser_id
7+
end
8+
9+
private
10+
11+
# Set a random browser ID for this browser.
12+
# TODO: check that this is unique before setting it.
13+
def set_browser_id
14+
unless cookies[:browser_id]
15+
cookies[:browser_id] = {:value => SecureRandom.uuid, :expires => 20.years.from_now}
16+
end
17+
end
18+
19+
# Return the currently logged in user object
20+
def current_user
21+
auth_session.user
22+
end
23+
24+
# Set the currently logged in user
25+
def current_user=(user)
26+
if user.is_a?(Authie.config.user_model_class_name.constantize)
27+
unless logged_in?
28+
@auth_session = Session.start(self, :user => user)
29+
end
30+
@current_user = user
31+
else
32+
auth_session.destroy if logged_in?
33+
@current_user = nil
34+
end
35+
end
36+
37+
# Is anyone currently logged in?
38+
def logged_in?
39+
auth_session.is_a?(Session)
40+
end
41+
42+
# Return the currently logged in user session
43+
def auth_session
44+
@auth_session ||= Session.get_session(self)
45+
end
46+
47+
end
48+
end

lib/authie/engine.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Authie
2+
class Engine < ::Rails::Engine
3+
4+
initializer 'authie.initialize' do |app|
5+
config.paths["db/migrate"].expanded.each do |expanded_path|
6+
app.config.paths["db/migrate"] << expanded_path
7+
end
8+
9+
ActiveSupport.on_load :active_record do
10+
require 'authie/session'
11+
end
12+
13+
ActiveSupport.on_load :action_controller do
14+
require 'authie/controller_extension'
15+
include Authie::ControllerExtension
16+
end
17+
18+
end
19+
20+
end
21+
end

lib/authie/error.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Authie
2+
class Error < StandardError
3+
end
4+
end

0 commit comments

Comments
 (0)