authlogic-connect 0.0.3.4 → 0.0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.markdown +156 -43
  2. data/Rakefile +1 -1
  3. data/lib/authlogic-connect.rb +2 -71
  4. data/lib/authlogic_connect/authlogic_connect.rb +46 -0
  5. data/lib/authlogic_connect/callback_filter.rb +1 -1
  6. data/lib/authlogic_connect/common.rb +1 -1
  7. data/lib/authlogic_connect/common/state.rb +16 -0
  8. data/lib/authlogic_connect/common/user.rb +102 -34
  9. data/lib/authlogic_connect/common/variables.rb +68 -16
  10. data/lib/authlogic_connect/engine.rb +0 -1
  11. data/lib/authlogic_connect/{common/ext.rb → ext.rb} +1 -0
  12. data/lib/authlogic_connect/oauth.rb +3 -1
  13. data/lib/authlogic_connect/oauth/helper.rb +17 -13
  14. data/lib/authlogic_connect/oauth/process.rb +61 -76
  15. data/lib/authlogic_connect/oauth/session.rb +3 -14
  16. data/lib/authlogic_connect/oauth/state.rb +54 -0
  17. data/lib/authlogic_connect/oauth/tokens/google_token.rb +9 -1
  18. data/lib/authlogic_connect/oauth/tokens/oauth_token.rb +67 -2
  19. data/lib/authlogic_connect/oauth/tokens/twitter_token.rb +2 -0
  20. data/lib/authlogic_connect/oauth/user.rb +57 -74
  21. data/lib/authlogic_connect/oauth/variables.rb +52 -27
  22. data/lib/authlogic_connect/openid.rb +3 -0
  23. data/lib/authlogic_connect/openid/process.rb +30 -0
  24. data/lib/authlogic_connect/openid/session.rb +6 -53
  25. data/lib/authlogic_connect/openid/state.rb +47 -0
  26. data/lib/authlogic_connect/openid/tokens/my_openid_token.rb +3 -0
  27. data/lib/authlogic_connect/openid/tokens/openid_token.rb +6 -0
  28. data/lib/authlogic_connect/openid/user.rb +38 -68
  29. data/lib/authlogic_connect/openid/variables.rb +17 -3
  30. data/lib/authlogic_connect/token.rb +0 -1
  31. data/lib/open_id_authentication.rb +0 -1
  32. data/rails/init.rb +1 -1
  33. data/test/controllers/test_users_controller.rb +21 -0
  34. data/test/libs/database.rb +48 -0
  35. data/test/libs/user.rb +3 -0
  36. data/test/libs/user_session.rb +2 -0
  37. data/test/old.rb +53 -0
  38. data/test/test_authlogic_connect.rb +1 -1
  39. data/test/test_helper.rb +142 -42
  40. data/test/test_user.rb +255 -0
  41. metadata +15 -4
@@ -1,21 +1,73 @@
1
- module AuthlogicConnect::Common
2
- module Variables
3
-
4
- def auth_controller
5
- is_auth_session? ? controller : session_class.controller
6
- end
7
-
8
- def auth_session
9
- auth_controller.session
1
+ module AuthlogicConnect::Common::Variables
2
+ include AuthlogicConnect::Common::State
3
+
4
+ def auth_controller
5
+ is_auth_session? ? controller : session_class.controller
6
+ end
7
+
8
+ def auth_session
9
+ auth_controller.session.symbolize_keys!
10
+ auth_controller.session.keys.each do |key|
11
+ auth_controller.session[key.to_s] = auth_controller.session.delete(key) if key.to_s =~ /^OpenID/
10
12
  end
11
-
12
- def auth_params
13
- auth_controller.params
13
+ auth_controller.session
14
+ end
15
+
16
+ def auth_params
17
+ auth_controller.params.symbolize_keys!
18
+ auth_controller.params.keys.each do |key|
19
+ auth_controller.params[key.to_s] = auth_controller.params.delete(key) if key.to_s =~ /^OpenID/
14
20
  end
15
-
16
- def is_auth_session?
17
- self.is_a?(Authlogic::Session::Base)
21
+ auth_controller.params
22
+ end
23
+
24
+ def auth_callback_url(options = {})
25
+ auth_controller.url_for({:controller => auth_controller.controller_name, :action => auth_controller.action_name}.merge(options))
26
+ end
27
+
28
+ # if we've said it's a "user" (registration), or a "session" (login)
29
+ def auth_type
30
+ from_session_or_params(:authentication_type)
31
+ end
32
+
33
+ # auth_params and auth_session attributes are all String!
34
+ def from_session_or_params(by)
35
+ key = by.is_a?(Symbol) ? by : by.to_sym
36
+ result = auth_params[key] if (auth_params && auth_params[key])
37
+ result = auth_session[key] if result.blank? # might be null here too
38
+ result
39
+ end
40
+
41
+ # because user and session are so closely tied together, I am still
42
+ # uncertain as to how they are saved. So this makes sure if we are
43
+ # logging in, it must be saving the session, otherwise the user.
44
+ def correct_request_class?
45
+ if is_auth_session?
46
+ auth_type.to_s == "session"
47
+ else
48
+ auth_type.to_s == "user"
18
49
  end
50
+ end
51
+
52
+ def add_session_key(key, value)
19
53
 
20
54
  end
21
- end
55
+
56
+ # because we may need to store 6+ session variables, all with pretty lengthy names,
57
+ # might as well just tinify them.
58
+ # just an idea
59
+ def optimized_session_key(key)
60
+ @optimized_session_keys ||= {
61
+ :auth_request_class => :authcl,
62
+ :authentication_method => :authme,
63
+ :authentication_type => :authty,
64
+ :oauth_provider => :authpr,
65
+ :auth_callback_method => :authcb,
66
+ :oauth_request_token => :authtk,
67
+ :oauth_request_token_secret => :authsc,
68
+ :auth_attributes => :authat
69
+ }
70
+ @optimized_session_keys[key]
71
+ end
72
+
73
+ end
@@ -1,6 +1,5 @@
1
1
  module AuthlogicConnect
2
2
  class Engine < Rails::Engine
3
- engine_name :authlogic_connect
4
3
 
5
4
  initializer "authlogic_connect.authentication_hook" do |app|
6
5
  app.middleware.use AuthlogicConnect::CallbackFilter
@@ -1,3 +1,4 @@
1
+ # these are extensions I've found useful for this project
1
2
  class String
2
3
  # normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
3
4
  def normalize_identifier
@@ -1,6 +1,7 @@
1
1
  module AuthlogicConnect::Oauth
2
2
  end
3
3
 
4
+ require File.dirname(__FILE__) + "/oauth/state"
4
5
  require File.dirname(__FILE__) + "/oauth/variables"
5
6
  require File.dirname(__FILE__) + "/oauth/process"
6
7
  require File.dirname(__FILE__) + "/oauth/user"
@@ -9,4 +10,5 @@ require File.dirname(__FILE__) + "/oauth/helper"
9
10
 
10
11
  ActiveRecord::Base.send(:include, AuthlogicConnect::Oauth::User)
11
12
  Authlogic::Session::Base.send(:include, AuthlogicConnect::Oauth::Session)
12
- ActionController::Base.helper AuthlogicConnect::Oauth::Helper
13
+ ActionController::Base.helper AuthlogicConnect::Oauth::Helper
14
+ ActionView::Helpers::FormBuilder.send(:include, AuthlogicConnect::Oauth::FormHelper)
@@ -1,16 +1,20 @@
1
- module AuthlogicConnect::Oauth
2
- module Helper
3
- def oauth_register_button(options = {})
4
- oauth_button('register_with_oauth', options)
5
- end
6
-
7
- def oauth_login_button(options = {})
8
- oauth_button('login_with_oauth', options)
9
- end
1
+ module AuthlogicConnect::Oauth::Helper
10
2
 
11
- private
12
- def oauth_button(name, options = {})
13
- "<input type='submit' value='#{options[:value]}' name='#{name}' id='user_submit' class='#{options[:class]}'/>"
14
- end
3
+ # options include "name"
4
+ def oauth_register_hidden_input
5
+ oauth_input(:type => "user")
15
6
  end
7
+
8
+ def oauth_login_hidden_input
9
+ oauth_input(:type => "session")
10
+ end
11
+
12
+ def oauth_input(options = {})
13
+ tag(:input, {:type => "hidden", :name => "authentication_type", :value => options[:type]})
14
+ end
15
+
16
+ end
17
+
18
+ module AuthlogicConnect::Oauth::FormHelper
19
+
16
20
  end
@@ -1,83 +1,68 @@
1
- module AuthlogicConnect::Oauth
2
- module Process
1
+ module AuthlogicConnect::Oauth::Process
3
2
 
4
- private
5
- include AuthlogicConnect::Oauth::Variables
3
+ include AuthlogicConnect::Oauth::Variables
6
4
 
7
- def validate_by_oauth
8
- validate_email_field = false
9
-
10
- if oauth_response.blank?
11
- redirect_to_oauth
12
- else
13
- authenticate_with_oauth
14
- end
15
- end
16
-
17
- def redirecting_to_oauth_server?
18
- authenticating_with_oauth? && oauth_response.blank?
19
- end
20
-
21
- def redirect_to_oauth
22
- save_oauth_callback
23
-
24
- if oauth_version == 1.0
25
- request = oauth_token.get_request_token(oauth_callback_url)
26
- save_auth_session(request)
27
- auth_controller.redirect_to request.authorize_url
28
- else
29
- auth_controller.redirect_to oauth_consumer.web_server.authorize_url(
30
- :redirect_uri => oauth_callback_url,
31
- :scope => oauth_token.config[:scope]
32
- )
33
- end
34
- end
35
-
36
- def save_oauth_callback
37
- puts "save_oauth_callback"
38
- # Store the class which is redirecting, so we can ensure other classes
39
- # don't get confused and attempt to use the response
40
- auth_session[:oauth_request_class] = self.class.name
41
- auth_session[:oauth_provider] = auth_params[:oauth_provider]
42
-
43
- # Tell our rack callback filter what method the current request is using
44
- auth_session[:auth_callback_method] = auth_controller.request.method
45
- end
46
-
47
- def save_auth_session(request)
48
- # store token and secret
49
- auth_session[:oauth_request_token] = request.token
50
- auth_session[:oauth_request_token_secret] = request.secret
51
- end
52
-
53
- def oauth_callback_url
54
- auth_controller.url_for :controller => auth_controller.controller_name, :action => auth_controller.action_name
5
+ # Step 2: after save is called, it runs this method for validation
6
+ def validate_by_oauth
7
+ validate_email_field = false
8
+ unless new_oauth_request? # shouldn't be validating if it's redirecting...
9
+ restore_attributes
10
+ complete_oauth_transaction
55
11
  end
56
-
57
- def request_token
58
- oauth_token.request_token(auth_session[:oauth_request_token], auth_session[:oauth_request_token_secret])
59
- end
60
-
61
- # in oauth 1.0, key = oauth_token, secret = oauth_secret
62
- # in oauth 2.0, key = code, secret = access_token
63
- def oauth_key_and_secret
64
- if oauth_version == 1.0
65
- result = request_token.get_access_token(:oauth_verifier => auth_params[:oauth_verifier])
66
- result = {:key => result.token, :secret => result.secret}
67
- else
68
- result = oauth_consumer.web_server.get_access_token(oauth_key, :redirect_uri => oauth_callback_url)
69
- result = {:key => result.token, :secret => oauth_key}
70
- end
71
- result
72
- end
73
-
74
- def generate_access_token
75
- if oauth_version == 1.0
76
- request_token.get_access_token(:oauth_verifier => auth_params[:oauth_verifier])
77
- else
78
- oauth_consumer.web_server.get_access_token(oauth_key, :redirect_uri => oauth_callback_url)
79
- end
12
+ end
13
+
14
+ # Step 3: if new_oauth_request?, redirect to oauth provider
15
+ def redirect_to_oauth
16
+ save_oauth_session
17
+ authorize_url = token_class.authorize_url(auth_callback_url) do |request_token|
18
+ save_auth_session_token(request_token) # only for oauth version 1
80
19
  end
20
+ auth_controller.redirect_to authorize_url
21
+ end
22
+
23
+ # Step 3a: save our passed-parameters into the session,
24
+ # so we can retrieve them after the redirect calls back
25
+ def save_oauth_session
26
+ # Store the class which is redirecting, so we can ensure other classes
27
+ # don't get confused and attempt to use the response
28
+ auth_session[:auth_request_class] = self.class.name
29
+
30
+ auth_session[:authentication_type] = auth_params[:authentication_type]
31
+ auth_session[:oauth_provider] = auth_params[:oauth_provider]
32
+ auth_session[:auth_method] = "oauth"
81
33
 
34
+ # Tell our rack callback filter what method the current request is using
35
+ auth_session[:auth_callback_method] = auth_controller.request.method
36
+ end
37
+
38
+ # Step 3b (if version 1.0 of oauth)
39
+ def save_auth_session_token(request)
40
+ # store token and secret
41
+ auth_session[:oauth_request_token] = request.token
42
+ auth_session[:oauth_request_token_secret] = request.secret
43
+ end
44
+
45
+ def restore_attributes
82
46
  end
47
+
48
+ # Step 4: on callback, run this method
49
+ def authenticate_with_oauth
50
+ # implemented in User and Session Oauth modules
51
+ end
52
+
53
+ # Step last, after the response
54
+ # having lots of trouble testing logging and out multiple times,
55
+ # so there needs to be a solid way to know when a user has messed up loggin in.
56
+ def cleanup_oauth_session
57
+ [:auth_request_class,
58
+ :authentication_type,
59
+ :auth_method,
60
+ :auth_attributes,
61
+ :oauth_provider,
62
+ :auth_callback_method,
63
+ :oauth_request_token,
64
+ :oauth_request_token_secret
65
+ ].each {|key| auth_session.delete(key)}
66
+ end
67
+
83
68
  end
@@ -37,26 +37,15 @@ module AuthlogicConnect::Oauth
37
37
  return block.nil?
38
38
  end
39
39
 
40
- def authenticating_with_oauth?
41
- return false unless oauth_provider
42
-
43
- # Initial request when user presses one of the button helpers
44
- initial_request = (controller.params && !controller.params[:login_with_oauth].blank?)
45
- # When the oauth provider responds and we made the initial request
46
- initial_response = (oauth_response && auth_session && auth_session[:oauth_request_class] == self.class.name)
47
-
48
- return initial_request || initial_response
49
- end
50
-
51
- def authenticate_with_oauth
40
+ def complete_oauth_transaction
52
41
  if @record
53
42
  self.attempted_record = record
54
43
  else
55
44
  # this generated token is always the same for a user!
56
45
  # this is searching with User.find ...
57
46
  # attempted_record is part of AuthLogic
58
- key = oauth_key_and_secret[:key]
59
- token = oauth_token.find_by_key(key, :include => [:user]) # some weird error if I leave out the include
47
+ hash = oauth_token_and_secret
48
+ token = token_class.find_by_key_or_token(hash[:key], hash[:token], :include => [:user]) # some weird error if I leave out the include)
60
49
  self.attempted_record = token.user
61
50
  end
62
51
 
@@ -0,0 +1,54 @@
1
+ # all these methods must return true or false
2
+ module AuthlogicConnect::Oauth::State
3
+
4
+ # 1. to call
5
+ # checks that we just passed parameters to it,
6
+ # and that the parameters say 'authentication_method' == 'oauth'
7
+ def oauth_request?
8
+ !auth_params.nil? && oauth_provider?
9
+ end
10
+
11
+ # 2. from call
12
+ # checks that the correct session variables are there
13
+ def oauth_response?
14
+ !oauth_response.nil? && !auth_session.nil? && auth_session[:auth_request_class] == self.class.name && auth_session[:auth_method] == "oauth"
15
+ end
16
+ alias_method :oauth_complete?, :oauth_response?
17
+
18
+ # 3. either to or from call
19
+ def using_oauth?
20
+ oauth_request? || oauth_response?
21
+ end
22
+
23
+ def new_oauth_request?
24
+ oauth_response.blank?
25
+ end
26
+
27
+ def oauth_provider?
28
+ !oauth_provider.nil? && !oauth_provider.empty?
29
+ end
30
+
31
+ # main method we call on validation
32
+ def authenticating_with_oauth?
33
+ correct_request_class? && using_oauth?
34
+ end
35
+
36
+ def allow_oauth_redirect?
37
+ authenticating_with_oauth? && !oauth_complete?
38
+ end
39
+
40
+ # both checks if it can redirect, and does the redirect.
41
+ # is there a more concise way to do this?
42
+ def redirecting_to_oauth_server?
43
+ if allow_oauth_redirect?
44
+ redirect_to_oauth
45
+ return true
46
+ end
47
+ return false
48
+ end
49
+
50
+ def validate_password_with_oauth?
51
+ !using_oauth? && require_password?
52
+ end
53
+
54
+ end
@@ -1,5 +1,8 @@
1
+ # http://code.google.com/apis/accounts/docs/OAuth_ref.html
2
+ # http://code.google.com/apis/accounts/docs/OpenID.html#settingup
1
3
  # http://code.google.com/apis/accounts/docs/OAuth.html
2
4
  # http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html
5
+ # http://www.manu-j.com/blog/add-google-oauth-ruby-on-rails-sites/214/
3
6
  # http://googlecodesamples.com/oauth_playground/
4
7
  # Scopes:
5
8
  # Analytics https://www.google.com/analytics/feeds/
@@ -30,4 +33,9 @@ class GoogleToken < OauthToken
30
33
  :access_token_path => "/accounts/OAuthGetAccessToken",
31
34
  :scope => "https://www.google.com/m8/feeds/"
32
35
 
33
- end
36
+ key do |access_token|
37
+ body = JSON.parse(access_token.get("https://www.google.com/m8/feeds/contacts/default/full?alt=json&max-results=0").body)
38
+ email = body["feed"]["author"].first["email"]["$t"] # $t is some weird google json thing
39
+ end
40
+
41
+ end
@@ -3,9 +3,9 @@ class OauthToken < Token
3
3
  def client
4
4
  unless @client
5
5
  if oauth_version == 1.0
6
- @client = OAuth::AccessToken.new(self.consumer, self.key, self.secret)
6
+ @client = OAuth::AccessToken.new(self.consumer, self.token, self.secret)
7
7
  else
8
- @client = OAuth2::AccessToken.new(self.consumer, self.key)
8
+ @client = OAuth2::AccessToken.new(self.consumer, self.token)
9
9
  end
10
10
  end
11
11
 
@@ -22,6 +22,7 @@ class OauthToken < Token
22
22
 
23
23
  class << self
24
24
 
25
+ # oauth version, 1.0 or 2.0
25
26
  def version(value)
26
27
  @oauth_version = value
27
28
  end
@@ -30,6 +31,22 @@ class OauthToken < Token
30
31
  @oauth_version ||= 1.0
31
32
  end
32
33
 
34
+ # unique key that we will use from the AccessToken response
35
+ # to identify the user by.
36
+ # in Twitter, its "user_id". Twitter has "screen_name", but that's
37
+ # more subject to change than user_id. Pick whatever is least likely to change
38
+ def key(value = nil, &block)
39
+ if block_given?
40
+ @oauth_key = block
41
+ else
42
+ @oauth_key = value.is_a?(Symbol) ? value : value.to_sym
43
+ end
44
+ end
45
+
46
+ def oauth_key
47
+ @oauth_key
48
+ end
49
+
33
50
  def consumer
34
51
  unless @consumer
35
52
  if oauth_version == 1.0
@@ -42,6 +59,54 @@ class OauthToken < Token
42
59
  @consumer
43
60
  end
44
61
 
62
+ # if we're lucky we can find it by the token.
63
+ def find_by_key_or_token(key, token, options = {})
64
+ result = self.find_by_key(key, options) unless key.nil?
65
+ unless result
66
+ result = self.find_by_token(token, options) unless token.nil?
67
+ end
68
+ result
69
+ end
70
+
71
+ # this is a wrapper around oauth 1 and 2.
72
+ # it looks obscure, but from the api point of view
73
+ # you won't have to worry about it's implementation.
74
+ # in oauth 1.0, key = oauth_token, secret = oauth_secret
75
+ # in oauth 2.0, key = code, secret = access_token
76
+ def get_token_and_secret(options = {})
77
+ oauth_verifier = options[:oauth_verifier]
78
+ redirect_uri = options[:redirect_uri]
79
+ token = options[:token]
80
+ secret = options[:secret]
81
+ if oauth_version == 1.0
82
+ access = request_token(token, secret).get_access_token(:oauth_verifier => oauth_verifier)
83
+ result = {:token => access.token, :secret => access.secret, :key => nil}
84
+ if self.oauth_key
85
+ if oauth_key.is_a?(Proc)
86
+ result[:key] = oauth_key.call(access)
87
+ else
88
+ result[:key] = access.params[self.oauth_key] || access.params[self.oauth_key.to_s] # try both
89
+ end
90
+ end
91
+ else
92
+ access = consumer.web_server.get_access_token(secret, :redirect_uri => redirect_uri)
93
+ result = {:token => access.token, :secret => secret, :key => nil}
94
+ end
95
+ result
96
+ end
97
+
98
+ # this is a cleaner method so we can access the authorize_url
99
+ # from oauth 1 or 2
100
+ def authorize_url(callback_url, &block)
101
+ if oauth_version == 1.0
102
+ request = get_request_token(callback_url)
103
+ yield request if block_given?
104
+ return request.authorize_url
105
+ else
106
+ return consumer.web_server.authorize_url(:redirect_uri => callback_url, :scope => self.config[:scope])
107
+ end
108
+ end
109
+
45
110
  def request_token(token, secret)
46
111
  OAuth::RequestToken.new(consumer, token, secret)
47
112
  end