This repository has been archived by the owner on Oct 20, 2020. It is now read-only.
/
oauth_login.rb
140 lines (114 loc) · 3.8 KB
/
oauth_login.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
require 'twitter'
require 'rack/request'
require 'hashie/mash'
class Twitter::OAuthLogin
attr_reader :options
DEFAULTS = {
:login_path => '/login', :return_to => '/',
:site => 'http://twitter.com', :authorize_path => '/oauth/authenticate'
}
def initialize(app, options)
@app = app
@options = DEFAULTS.merge options
end
def call(env)
request = Request.new(env)
if request.get? and request.path == options[:login_path]
# detect if Twitter redirected back here
if request[:oauth_verifier]
handle_twitter_authorization(request) do
@app.call(env)
end
elsif request[:denied]
# user refused to log in with Twitter, so give up
redirect_to_return_path(request)
else
# user clicked to login; send them to Twitter
redirect_to_twitter(request)
end
else
@app.call(env)
end
end
module Helpers
def twitter_consumer
token = OAuth::AccessToken.new(oauth_consumer, *session[:access_token])
Twitter::Base.new token
end
def oauth_consumer
OAuth::Consumer.new(*session[:oauth_consumer])
end
def twitter_user
if session[:twitter_user]
Hashie::Mash[session[:twitter_user]]
end
end
def twitter_logout
[:oauth_consumer, :access_token, :twitter_user].each do |key|
session[key] = nil # work around a Rails 2.3.5 bug
session.delete key
end
end
end
class Request < Rack::Request
# for storing :request_token, :access_token
def session
env['rack.session'] ||= {}
end
# SUCKS: must duplicate logic from the `url` method
def url_for(path)
url = scheme + '://' + host
if scheme == 'https' && port != 443 ||
scheme == 'http' && port != 80
url << ":#{port}"
end
url << path
end
end
protected
def redirect_to_twitter(request)
# create a request token and store its parameter in session
token = oauth_consumer.get_request_token(:oauth_callback => request.url)
request.session[:request_token] = [token.token, token.secret]
# redirect to Twitter authorization page
redirect token.authorize_url
end
def handle_twitter_authorization(request)
access_token = get_access_token(request)
# get and store authenticated user's info from Twitter
twitter = Twitter::Base.new access_token
request.session[:twitter_user] = twitter.verify_credentials.to_hash
# pass the request down to the main app
response = yield
# check if the app implemented anything at :login_path
if response[0].to_i == 404
# if not, redirect to :return_to path
redirect_to_return_path(request)
else
# use the response from the app without modification
response
end
end
private
def get_access_token(request)
# replace the request token in session with access token
request_token = ::OAuth::RequestToken.new(oauth_consumer, *request.session[:request_token])
access_token = request_token.get_access_token(:oauth_verifier => request[:oauth_verifier])
# store access token and OAuth consumer parameters in session
request.session.delete(:request_token)
request.session[:access_token] = [access_token.token, access_token.secret]
consumer = access_token.consumer
request.session[:oauth_consumer] = [consumer.key, consumer.secret, consumer.options]
return access_token
end
def redirect_to_return_path(request)
redirect request.url_for(options[:return_to])
end
def redirect(url)
["302", {'Location' => url, 'Content-type' => 'text/plain'}, []]
end
def oauth_consumer
::OAuth::Consumer.new options[:key], options[:secret],
:site => options[:site], :authorize_path => options[:authorize_path]
end
end