mbleigh-twitter-auth 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +52 -0
- data/VERSION.yml +4 -0
- data/generators/twitter_auth_migration/templates/migration.rb +24 -0
- data/generators/twitter_auth_migration/twitter_auth_migration_generator.rb +7 -0
- data/lib/twitter_auth.rb +5 -0
- data/lib/twitter_auth/controller_extensions.rb +200 -0
- data/lib/twitter_auth/cryptify.rb +28 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/twitter_auth_spec.rb +1 -0
- data/test/test_helper.rb +10 -0
- data/test/twitter_auth_test.rb +7 -0
- metadata +67 -0
data/README.markdown
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
TwitterAuth
|
2
|
+
===========
|
3
|
+
|
4
|
+
TwitterAuth is a plugin to provide a standard authentication stack using Twitter
|
5
|
+
as an SSO provider. This is obviously most useful and therefore targeted at
|
6
|
+
apps that intend to heavily use the Twitter API.
|
7
|
+
|
8
|
+
**Note:** TwitterAuth uses Rails Engines functionality from Rails 2.3 and is
|
9
|
+
therefore incompatible with earlier versions of Rails.
|
10
|
+
|
11
|
+
Getting Started
|
12
|
+
---------------
|
13
|
+
|
14
|
+
First, install either by gem or by plugin:
|
15
|
+
|
16
|
+
config.gem 'mbleigh-twitter-auth', :source => 'http://gems.github.com/'
|
17
|
+
|
18
|
+
OR
|
19
|
+
|
20
|
+
script/plugin install git://github.com/mbleigh/twitter-auth.git
|
21
|
+
|
22
|
+
Next, to get started, you will need to generate the migration for the User
|
23
|
+
model that TwitterAuth uses. It's simple:
|
24
|
+
|
25
|
+
script/generate migration twitter_auth_migration
|
26
|
+
|
27
|
+
If you look in the migration you will see that there are some information
|
28
|
+
fields pre-populated (name, location, etc). These will automatically be
|
29
|
+
retrieved from Twitter at each login and therefor kept both accessible
|
30
|
+
and fresh for your usage.
|
31
|
+
|
32
|
+
Believe it or not, that's it! You now have access to the standard suite
|
33
|
+
of restful-auth controller helpers such as:
|
34
|
+
|
35
|
+
* login_required
|
36
|
+
* current_user
|
37
|
+
* logged_in?
|
38
|
+
|
39
|
+
And you also have the ability to login through the built-in SessionController.
|
40
|
+
Just run the app and point your browser to '/login' to get started! You don't
|
41
|
+
need to sign up because it will automatically create new users if the logging
|
42
|
+
in user has never logged in before.
|
43
|
+
|
44
|
+
Caveats
|
45
|
+
-------
|
46
|
+
|
47
|
+
This is **extremely alpha** code and has not been thoroughly spec'ed or even
|
48
|
+
inspected. Use at your own risk as the functionality is likely to change
|
49
|
+
drastically even in the near future.
|
50
|
+
|
51
|
+
|
52
|
+
Copyright (c) 2009 [Michael Bleigh](http://www.mbleigh.com) and [Intridea, Inc.](http://www.intridea.com/), released under the MIT license
|
data/VERSION.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class TwitterAuthMigration < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :users do |t|
|
4
|
+
t.string :login
|
5
|
+
t.string :crypted_password
|
6
|
+
t.string :salt
|
7
|
+
|
8
|
+
# Basic info pulled automatically from Twitter.
|
9
|
+
# Feel free to remove any of these columns you don't want.
|
10
|
+
t.string :name
|
11
|
+
t.string :location
|
12
|
+
t.text :description
|
13
|
+
t.string :profile_image_url
|
14
|
+
t.string :url
|
15
|
+
t.boolean :protected
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.down
|
22
|
+
drop_table :users
|
23
|
+
end
|
24
|
+
end
|
data/lib/twitter_auth.rb
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
module TwitterAuth
|
2
|
+
# The extensions to ActionController to enable our
|
3
|
+
# usage of Twitter-based authentication. This is
|
4
|
+
# taken directly from restful-authentication, so
|
5
|
+
# no credit is due here.
|
6
|
+
module ControllerExtensions
|
7
|
+
# Returns true or false if the user is logged in.
|
8
|
+
# Preloads @current_user with the user model if they're logged in.
|
9
|
+
def logged_in?
|
10
|
+
!!current_user
|
11
|
+
end
|
12
|
+
|
13
|
+
# Accesses the current user from the session.
|
14
|
+
# Future calls avoid the database because nil is not equal to false.
|
15
|
+
def current_user
|
16
|
+
@current_user ||= (login_from_session || login_from_basic_auth) unless @current_user == false
|
17
|
+
end
|
18
|
+
|
19
|
+
# Store the given user id in the session.
|
20
|
+
def current_user=(new_user)
|
21
|
+
session[:user_id] = new_user ? new_user.id : nil
|
22
|
+
@current_user = new_user || false
|
23
|
+
end
|
24
|
+
|
25
|
+
# Check if the user is authorized
|
26
|
+
#
|
27
|
+
# Override this method in your controllers if you want to restrict access
|
28
|
+
# to only a few actions or if you want to check if the user
|
29
|
+
# has the correct rights.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
#
|
33
|
+
# # only allow nonbobs
|
34
|
+
# def authorized?
|
35
|
+
# current_user.login != "bob"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
def authorized?(action=nil, resource=nil, *args)
|
39
|
+
logged_in?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Filter method to enforce a login requirement.
|
43
|
+
#
|
44
|
+
# To require logins for all actions, use this in your controllers:
|
45
|
+
#
|
46
|
+
# before_filter :login_required
|
47
|
+
#
|
48
|
+
# To require logins for specific actions, use this in your controllers:
|
49
|
+
#
|
50
|
+
# before_filter :login_required, :only => [ :edit, :update ]
|
51
|
+
#
|
52
|
+
# To skip this in a subclassed controller:
|
53
|
+
#
|
54
|
+
# skip_before_filter :login_required
|
55
|
+
#
|
56
|
+
def login_required
|
57
|
+
authorized? || access_denied
|
58
|
+
end
|
59
|
+
|
60
|
+
# Redirect as appropriate when an access request fails.
|
61
|
+
#
|
62
|
+
# The default action is to redirect to the login screen.
|
63
|
+
#
|
64
|
+
# Override this method in your controllers if you want to have special
|
65
|
+
# behavior in case the user is not authorized
|
66
|
+
# to access the requested action. For example, a popup window might
|
67
|
+
# simply close itself.
|
68
|
+
def access_denied
|
69
|
+
respond_to do |format|
|
70
|
+
format.html do
|
71
|
+
store_location
|
72
|
+
redirect_to new_session_path
|
73
|
+
end
|
74
|
+
# format.any doesn't work in rails version < http://dev.rubyonrails.org/changeset/8987
|
75
|
+
# you may want to change format.any to e.g. format.any(:js, :xml)
|
76
|
+
format.any(:xml, :json) do
|
77
|
+
request_http_basic_authentication 'Web Password'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Store the URI of the current request in the session.
|
83
|
+
#
|
84
|
+
# We can return to this location by calling #redirect_back_or_default.
|
85
|
+
def store_location
|
86
|
+
session[:return_to] = request.request_uri
|
87
|
+
end
|
88
|
+
|
89
|
+
# Redirect to the URI stored by the most recent store_location call or
|
90
|
+
# to the passed default. Set an appropriately modified
|
91
|
+
# after_filter :store_location, :only => [:index, :new, :show, :edit]
|
92
|
+
# for any controller you want to be bounce-backable.
|
93
|
+
def redirect_back_or_default(default=nil)
|
94
|
+
redirect_to(session[:return_to] || default || default_location)
|
95
|
+
session[:return_to] = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
# Override this method with what you want your 'default default'
|
99
|
+
# to be (where it will redirect if there's no back and no explicit
|
100
|
+
# default).
|
101
|
+
def default_location
|
102
|
+
'/'
|
103
|
+
end
|
104
|
+
|
105
|
+
# Inclusion hook to make #current_user and #logged_in?
|
106
|
+
# available as ActionView helper methods.
|
107
|
+
def self.included(base)
|
108
|
+
base.send :helper_method, :current_user, :logged_in?, :authorized? if base.respond_to? :helper_method
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Login
|
113
|
+
#
|
114
|
+
|
115
|
+
# Called from #current_user. First attempt to login by the user id stored in the session.
|
116
|
+
def login_from_session
|
117
|
+
self.current_user = User.find_by_id(session[:user_id]) if session[:user_id]
|
118
|
+
end
|
119
|
+
|
120
|
+
# Called from #current_user. Now, attempt to login by basic authentication information.
|
121
|
+
def login_from_basic_auth
|
122
|
+
authenticate_with_http_basic do |login, password|
|
123
|
+
self.current_user = User.authenticate(login, password)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# Logout
|
129
|
+
#
|
130
|
+
|
131
|
+
# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
|
132
|
+
# for the paranoid: we _should_ be storing user_token = hash(cookie_token, request IP)
|
133
|
+
# def login_from_cookie
|
134
|
+
# user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
|
135
|
+
# if user && user.remember_token?
|
136
|
+
# self.current_user = user
|
137
|
+
# handle_remember_cookie! false # freshen cookie token (keeping date)
|
138
|
+
# self.current_user
|
139
|
+
# end
|
140
|
+
# end
|
141
|
+
|
142
|
+
# This is ususally what you want; resetting the session willy-nilly wreaks
|
143
|
+
# havoc with forgery protection, and is only strictly necessary on login.
|
144
|
+
# However, **all session state variables should be unset here**.
|
145
|
+
def logout_keeping_session!
|
146
|
+
# Kill server-side auth cookie
|
147
|
+
@current_user.forget_me if @current_user.is_a? User
|
148
|
+
@current_user = false # not logged in, and don't do it for me
|
149
|
+
# kill_remember_cookie! # Kill client-side auth cookie
|
150
|
+
session[:user_id] = nil # keeps the session but kill our variable
|
151
|
+
# explicitly kill any other session variables you set
|
152
|
+
end
|
153
|
+
|
154
|
+
# The session should only be reset at the tail end of a form POST --
|
155
|
+
# otherwise the request forgery protection fails. It's only really necessary
|
156
|
+
# when you cross quarantine (logged-out to logged-in).
|
157
|
+
def logout_killing_session!
|
158
|
+
logout_keeping_session!
|
159
|
+
reset_session
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# Remember_me Tokens
|
164
|
+
#
|
165
|
+
# Cookies shouldn't be allowed to persist past their freshness date,
|
166
|
+
# and they should be changed at each login
|
167
|
+
|
168
|
+
# Cookies shouldn't be allowed to persist past their freshness date,
|
169
|
+
# and they should be changed at each login
|
170
|
+
|
171
|
+
# def valid_remember_cookie?
|
172
|
+
# return nil unless @current_user
|
173
|
+
# (@current_user.remember_token?) &&
|
174
|
+
# (cookies[:auth_token] == @current_user.remember_token)
|
175
|
+
# end
|
176
|
+
|
177
|
+
# Refresh the cookie auth token if it exists, create it otherwise
|
178
|
+
# def handle_remember_cookie! new_cookie_flag
|
179
|
+
# return unless @current_user
|
180
|
+
# case
|
181
|
+
# when valid_remember_cookie? then @current_user.refresh_token # keeping same expiry date
|
182
|
+
# when new_cookie_flag then @current_user.remember_me
|
183
|
+
# else @current_user.forget_me
|
184
|
+
# end
|
185
|
+
# send_remember_cookie!
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
# def kill_remember_cookie!
|
189
|
+
# cookies.delete :auth_token
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# def send_remember_cookie!
|
193
|
+
# cookies[:auth_token] = {
|
194
|
+
# :value => @current_user.remember_token,
|
195
|
+
# :expires => @current_user.remember_token_expires_at }
|
196
|
+
# end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
ActionController::Base.send :include, TwitterAuth::ControllerExtensions
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
module TwitterAuth
|
3
|
+
module Cryptify
|
4
|
+
class Error < StandardError; end
|
5
|
+
mattr_accessor :crypt_password
|
6
|
+
@@crypt_password = '--TwitterAuth-!##@--2ef'
|
7
|
+
|
8
|
+
def self.encrypt(data, salt)
|
9
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
10
|
+
cipher.encrypt
|
11
|
+
cipher.pkcs5_keyivgen(crypt_password, salt)
|
12
|
+
encrypted_data = cipher.update(data)
|
13
|
+
encrypted_data << cipher.final
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.decrypt(encrypted_data, salt)
|
17
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
18
|
+
cipher.decrypt
|
19
|
+
cipher.pkcs5_keyivgen(crypt_password, salt)
|
20
|
+
data = cipher.update(encrypted_data)
|
21
|
+
data << cipher.final
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.generate_salt
|
25
|
+
[rand(2**64 - 1)].pack("Q")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
begin
|
2
|
+
require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
|
3
|
+
rescue LoadError
|
4
|
+
puts "You need to install rspec in your base app"
|
5
|
+
exit
|
6
|
+
end
|
7
|
+
|
8
|
+
plugin_spec_dir = File.dirname(__FILE__)
|
9
|
+
ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
|
10
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mbleigh-twitter-auth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Bleigh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-06 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: TODO
|
17
|
+
email: michael@intridea.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.markdown
|
26
|
+
- VERSION.yml
|
27
|
+
- generators/twitter_auth_migration
|
28
|
+
- generators/twitter_auth_migration/templates
|
29
|
+
- generators/twitter_auth_migration/templates/migration.rb
|
30
|
+
- generators/twitter_auth_migration/twitter_auth_migration_generator.rb
|
31
|
+
- lib/twitter_auth
|
32
|
+
- lib/twitter_auth/controller_extensions.rb
|
33
|
+
- lib/twitter_auth/cryptify.rb
|
34
|
+
- lib/twitter_auth.rb
|
35
|
+
- test/test_helper.rb
|
36
|
+
- test/twitter_auth_test.rb
|
37
|
+
- spec/spec_helper.rb
|
38
|
+
- spec/twitter_auth_spec.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/mbleigh/twitter-auth
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --inline-source
|
44
|
+
- --charset=UTF-8
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.2.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: Standard authentication stack for Rails using Twitter to log in.
|
66
|
+
test_files: []
|
67
|
+
|