authlogic-connect 0.0.3.4 → 0.0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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