This repository has been archived by the owner on Sep 7, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
login.rb
167 lines (135 loc) · 4.56 KB
/
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
require 'oauth'
require 'yajl'
require 'rack/request'
require 'hashie/mash'
module Twitter
end
class Twitter::Login
attr_reader :options
class << self
attr_accessor :consumer_key, :secret, :site
end
DEFAULTS = {
:return_to => '/',
:site => 'http://api.twitter.com',
:authorize_path => '/oauth/authenticate'
}
def initialize(options)
@options = DEFAULTS.merge options
self.class.consumer_key, self.class.secret = @options[:consumer_key], @options[:secret]
self.class.site = @options[:site]
end
def login_handler(options = {})
@options.update options
return self
end
def call(env)
request = Request.new(env)
if request[:oauth_verifier]
# user authorized the app
handle_twitter_authorization(request)
elsif request[:denied]
# user refused to log in with Twitter
handle_denied_access(request)
else
# starting the login process; send user to Twitter
redirect_to_twitter(request)
end
end
module Helpers
def twitter_client
OAuth::AccessToken.new(twitter_oauth, *session[:twitter_access_token])
end
def twitter_oauth
OAuth::Consumer.new Twitter::Login.consumer_key, Twitter::Login.secret,
:site => Twitter::Login.site
end
def twitter_user
if session[:twitter_user]
Hashie::Mash[session[:twitter_user]]
end
end
def twitter_logout
[:twitter_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
def wants?(mime_type)
env['HTTP_ACCEPT'].to_s.include? mime_type
end
end
protected
def redirect_to_twitter(request)
# create a request token and store its parameter in session
request_token = oauth.get_request_token(:oauth_callback => request.url)
request.session[:twitter_request_token] = [request_token.token, request_token.secret]
# redirect to Twitter authorization page
if request.wants? 'application/json'
body = %({"authorize_url":"#{request_token.authorize_url}"})
["200", {'Content-type' => 'application/json'}, [body]]
elsif request.xhr? or request.wants? 'application/javascript'
body = "window.location.assign('#{request_token.authorize_url}')"
["200", {'Content-type' => 'application/javascript'}, [body]]
else
redirect request_token.authorize_url
end
end
def handle_twitter_authorization(request)
access_token = authorize_from_request(request)
# get and store authenticated user's info from Twitter
response = access_token.get('/1/account/verify_credentials.json')
request.session[:twitter_user] = user_hash_from_response(response)
redirect_to_return_path(request)
end
def handle_denied_access(request)
# cleanup session and set an error identifier
request.session[:twitter_request_token] = nil # work around a Rails 2.3.5 bug
request.session.delete(:twitter_request_token)
request.session[:twitter_error] = 'user_denied'
redirect_to_return_path(request)
end
private
# replace the request token in session with access token
def authorize_from_request(request)
rtoken, rsecret = request.session[:twitter_request_token]
request_token = OAuth::RequestToken.new(oauth, rtoken, rsecret)
request_token.get_access_token(:oauth_verifier => request[:oauth_verifier]).tap do |access_token|
request.session.delete(:twitter_request_token)
request.session[:twitter_access_token] = [access_token.token, access_token.secret]
end
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
OAuth::Consumer.new options[:consumer_key], options[:secret],
:site => options[:site], :authorize_path => options[:authorize_path]
end
def user_hash_from_response(api_response)
parse_response(api_response).reject { |key, _|
key == 'status' or key =~ /^profile_|_color$/
}
end
def parse_response(api_response)
Yajl::Parser.parse api_response.body
end
end