omniauth-multiprovider 0.1.1 → 0.2.0

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +13 -18
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/{LICENSE → LICENSE.txt} +0 -0
  8. data/README.md +52 -7
  9. data/Rakefile +2 -0
  10. data/lib/omniauth-multiprovider.rb +3 -1
  11. data/lib/omniauth/multiprovider.rb +0 -1
  12. data/lib/omniauth/multiprovider/config.rb +25 -0
  13. data/lib/omniauth/multiprovider/controllers/callbacks_controller.rb +43 -3
  14. data/lib/omniauth/multiprovider/error.rb +24 -1
  15. data/lib/omniauth/multiprovider/models/active_record.rb +14 -0
  16. data/lib/omniauth/multiprovider/models/authentication.rb +1 -9
  17. data/lib/omniauth/multiprovider/models/concerns/omni_authenticable.rb +79 -32
  18. data/lib/omniauth/multiprovider/version.rb +1 -1
  19. data/lib/omniauth/provider/abstract.rb +6 -38
  20. data/lib/omniauth/provider/generic.rb +10 -13
  21. data/omniauth-multiprovider.gemspec +15 -2
  22. data/spec/omniauth/error_spec.rb +30 -0
  23. data/spec/omniauth/multiprovider/controllers/callbacks_controller_spec.rb +90 -0
  24. data/spec/omniauth/multiprovider/models/active_record_spec.rb +21 -0
  25. data/spec/omniauth/multiprovider/models/concerns/omni_authenticable_spec.rb +67 -0
  26. data/spec/omniauth/provider/abstract_spec.rb +15 -0
  27. data/spec/omniauth/provider/facebook_spec.rb +13 -0
  28. data/spec/omniauth/provider/generic_spec.rb +13 -0
  29. data/spec/rails_app/.gitignore +17 -0
  30. data/spec/rails_app/Rakefile +6 -0
  31. data/spec/rails_app/app/assets/images/.keep +0 -0
  32. data/spec/rails_app/app/assets/javascripts/application.js +16 -0
  33. data/spec/rails_app/app/assets/stylesheets/application.css +15 -0
  34. data/spec/rails_app/app/controllers/application_controller.rb +5 -0
  35. data/spec/rails_app/app/controllers/concerns/.keep +0 -0
  36. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  37. data/spec/rails_app/app/mailers/.keep +0 -0
  38. data/spec/rails_app/app/models/.keep +0 -0
  39. data/spec/rails_app/app/models/concerns/.keep +0 -0
  40. data/spec/rails_app/app/models/user.rb +4 -0
  41. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  42. data/spec/rails_app/bin/bundle +3 -0
  43. data/spec/rails_app/bin/rails +4 -0
  44. data/spec/rails_app/bin/rake +4 -0
  45. data/spec/rails_app/bin/setup +29 -0
  46. data/spec/rails_app/config.ru +4 -0
  47. data/spec/rails_app/config/application.rb +26 -0
  48. data/spec/rails_app/config/boot.rb +3 -0
  49. data/spec/rails_app/config/database.yml +25 -0
  50. data/spec/rails_app/config/environment.rb +5 -0
  51. data/spec/rails_app/config/environments/test.rb +42 -0
  52. data/spec/rails_app/config/initializers/assets.rb +11 -0
  53. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  54. data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
  55. data/spec/rails_app/config/initializers/devise.rb +259 -0
  56. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  57. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  58. data/spec/rails_app/config/initializers/mime_types.rb +4 -0
  59. data/spec/rails_app/config/initializers/session_store.rb +3 -0
  60. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  61. data/spec/rails_app/config/locales/en.yml +23 -0
  62. data/spec/rails_app/config/routes.rb +3 -0
  63. data/spec/rails_app/config/secrets.yml +22 -0
  64. data/spec/rails_app/db/seeds.rb +7 -0
  65. data/spec/rails_app/lib/assets/.keep +0 -0
  66. data/spec/rails_app/lib/tasks/.keep +0 -0
  67. data/spec/rails_app/public/404.html +67 -0
  68. data/spec/rails_app/public/422.html +67 -0
  69. data/spec/rails_app/public/500.html +66 -0
  70. data/spec/rails_app/public/favicon.ico +0 -0
  71. data/spec/rails_app/public/robots.txt +5 -0
  72. data/spec/spec_helper.rb +43 -0
  73. data/spec/support/shared_examples_for_providers.rb +27 -0
  74. metadata +226 -7
  75. data/lib/omniauth/multiprovider/helpers.rb +0 -23
  76. data/lib/omniauth/provider/guests.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96e73d3f9fb27f6b97a817cd686171e911d5060e
4
- data.tar.gz: ed5755e6cb69e323a80f6052e62824f14951c669
3
+ metadata.gz: c1f3a5a4a52bb8284020af1e7399416e1b1b34d6
4
+ data.tar.gz: 15ec2a53af0a3a883291ad7c0c8dcb3772c19bef
5
5
  SHA512:
6
- metadata.gz: d7397943a451ddd556ef854f442aad422ccc11e8be4ee0b31483c7921a7e2a8d936eb2f8350b98a34436eb6c5fa8ac628dfccf877599fee5e64b10c6c993b7c5
7
- data.tar.gz: fe84283f3ae92745c71912739c8bf10d02a48ec726f45e460854e37ad072f63ef265c286e1f853002bb0c335074284935dbbebbf9fccb3cca64062a7d7bf779b
6
+ metadata.gz: cda70641cc3aea4b3998bb02e13b22cf9bb0e77dd7bf459d4bb9496841392489be746885a0a402f1a2d1d5da5b5fdf3a86b186b3cc0d792ddca611c90d65789a
7
+ data.tar.gz: 4ba92b422f566f0b1f7e2e3c88578c05a3e51b939ed2469c6616f28e01f10ee56e2ec1db0d02738c3f8e2c2d95a90dd95ef1fb1a9becf8789322db1ce65b40f5
data/.gitignore CHANGED
@@ -1,19 +1,14 @@
1
- *.rbc
2
- *.sassc
3
- .sass-cache
4
- capybara-*.html
5
- .rspec
6
- .rvmrc
7
- /.bundle
8
- /vendor/bundle
9
- /log/*
10
- /tmp/*
11
- /db/*.sqlite3
12
- /public/system/*
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
13
5
  /coverage/
14
- /spec/tmp/*
15
- **.orig
16
- rerun.txt
17
- pickle-email-*.html
18
- .project
19
- config/initializers/secret_token.rb
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1 @@
1
+ omniauth-multiprovider
@@ -0,0 +1 @@
1
+ ruby-2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in omniauth-multiprovider.gemspec
4
+ gemspec
File without changes
data/README.md CHANGED
@@ -1,17 +1,33 @@
1
- omniauth-multiprovider
2
- ======================
1
+ # Omniauth::Multiprovider
2
+
3
3
 
4
4
  An easy way to authenticate users through many oauth providers (i.e. facebook, twitter, github and custom providers)
5
5
 
6
6
  No more OmniauthCallbacksController, no more complex method to relate users with tokens
7
7
 
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'omniauth-multiprovider'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install omniauth-multiprovider
23
+
8
24
  ## Disclaimer
9
25
 
10
26
  This is a work in progress. Expect serious refactors, breaking changes.
11
27
 
12
- I am almose sure that this gem will not work outside an Rails project. Sorry Sinatra lovers... I will try to remove any DHH opinion ;)
28
+ I am almost sure that this gem will not work outside an Rails project. Sorry Sinatra lovers... I will try to remove any DHH opinion ;)
13
29
 
14
- ## How to use
30
+ ## Usage
15
31
 
16
32
  In your devise resource model (aka mapping), usually `User` add:
17
33
 
@@ -32,26 +48,55 @@ Create a migration to add the `Authentication` model with:
32
48
  The change method should contain something like:
33
49
 
34
50
  create_table :authentications do |t|
35
- t.references :{devise_mapping_name}
51
+ t.references :{devise_mapping_name}, polymorphic: true
36
52
  t.string :uid, null: false
37
53
  t.string :provider, null: false
38
54
  t.string :access_token
39
55
  t.string :permissions
40
56
  t.timestamps
41
57
  end
42
-
58
+
43
59
  add_index :authentications, [:provider, :uid], unique: true
44
60
 
45
61
  **devise_mapping_name** is probably `user`
46
62
 
63
+ ### Errors and handling
64
+
65
+ There are 3 error scenarios:
66
+
67
+ * The oauth authentication already exists and belongs to the current logged-in user (it's trying to reconnect)
68
+ * The oauth authentication already exists and belongs to a different user (potential identity theft attemp)
69
+ * The oauth-provided email is already in use
70
+
71
+ The first error will be ignored by default.
72
+
73
+ For the other two errors `OmniAuth::MultiProvider::CallbacksController` will set the `flash[:alert]` to the localized I18N keys:
74
+
75
+ * common prefix: `devise.callbacks.user`
76
+ * `bound_to_other`
77
+ * `email_taken`
78
+
79
+
47
80
  ## Testing
48
81
 
49
- I am porting this gem from an existing project, the testing is currently embebed in the project. I will extract those test and cover this project.
82
+ Run `rspec`.
83
+
84
+ More tests are appreciated.
50
85
 
51
86
  ##Contributors
52
87
 
53
88
  [German DZ](https://twitter.com/GermanDZ)
89
+ [Abel Muiño](https://twitter.com/amuino)
54
90
 
55
91
  ## License
56
92
 
57
93
  MIT License.
94
+
95
+
96
+ ## Contributing
97
+
98
+ 1. Fork it ( https://github.com/[my-github-username]/omniauth-multiprovider/fork )
99
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
100
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
101
+ 4. Push to the branch (`git push origin my-new-feature`)
102
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -1,4 +1,5 @@
1
1
  require 'omniauth/multiprovider'
2
+ require 'omniauth/multiprovider/models/active_record'
2
3
 
3
4
  autoload :Authentication, 'omniauth/multiprovider/models/authentication'
4
5
  module OmniAuth
@@ -7,11 +8,12 @@ module OmniAuth
7
8
  autoload :OmniAuthenticable, 'omniauth/multiprovider/models/concerns/omni_authenticable'
8
9
  autoload :EmailMockups, 'omniauth/multiprovider/models/email_mockups'
9
10
  autoload :Error, 'omniauth/multiprovider/error'
11
+ autoload :AlreadyBoundError, 'omniauth/multiprovider/error'
10
12
  end
11
13
  module Provider
12
14
  autoload :Abstract, 'omniauth/provider/abstract'
13
15
  autoload :Generic, 'omniauth/provider/generic'
14
16
  autoload :Facebook, 'omniauth/provider/facebook'
15
- autoload :Guests, 'omniauth/provider/guests'
16
17
  end
17
18
  end
19
+
@@ -1,2 +1 @@
1
- require 'omniauth/multiprovider/helpers'
2
1
  require 'omniauth/multiprovider/version'
@@ -0,0 +1,25 @@
1
+ module OmniAuth
2
+ module MultiProvider
3
+ class Config
4
+ def authentication_klass
5
+ Authentication
6
+ end
7
+
8
+ def resource_klass
9
+ User
10
+ end
11
+
12
+ def resource_mapping
13
+ :user
14
+ end
15
+
16
+ def current_resource
17
+ "current_#{resource_mapping}".to_sym
18
+ end
19
+
20
+ def authentication_relationship_name
21
+ :authentications
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,9 +1,49 @@
1
1
  module OmniAuth
2
2
  module MultiProvider
3
3
  class CallbacksController < Devise::OmniauthCallbacksController
4
- def self.add_callback(provider, &block)
5
- self.send :define_method, provider, &block
4
+
5
+ protected
6
+
7
+ def self.create_handler(provider_name)
8
+ provider_class =
9
+ begin
10
+ "OmniAuth::Provider::#{provider_name.to_s.camelize}".constantize
11
+ rescue NameError
12
+ OmniAuth::Provider::Generic
13
+ end
14
+ define_method provider_name do
15
+ memo_name = "@_#{provider_name}"
16
+ provider = instance_variable_get memo_name
17
+ unless provider.present?
18
+ provider = provider_class.new(self)
19
+ instance_variable_set memo_name, provider
20
+ end
21
+ handle_request provider
22
+ end
23
+ end
24
+
25
+ def handle_request(provider)
26
+ provider.handle_request
27
+ rescue OmniAuth::MultiProvider::Error => error
28
+ raise unless handle_provider_error(error)
29
+ rescue RuntimeError => error
30
+ raise unless handle_unexpected_error(error)
31
+ end
32
+
33
+ def handle_provider_error(error)
34
+ set_flash_message :alert, error.message
35
+ redirect_to after_sign_in_path_for(resource_name)
36
+ true
37
+ end
38
+
39
+ def handle_unexpected_error(error)
40
+ false # no explicit handling
41
+ end
42
+
43
+ # TODO: this looks like a good candidate for using method_missing
44
+ Devise.omniauth_providers.each do |provider_name|
45
+ create_handler provider_name
6
46
  end
7
47
  end
8
48
  end
9
- end
49
+ end
@@ -2,5 +2,28 @@ module OmniAuth
2
2
  module MultiProvider
3
3
  class Error < RuntimeError
4
4
  end
5
+
6
+ class AlreadyBoundError < Error
7
+ def initialize(current, bound_to)
8
+ @current = current
9
+ @bound_to = bound_to
10
+ end
11
+
12
+ def message
13
+ if @current == @bound_to
14
+ 'bound_to_same'
15
+ else
16
+ 'bound_to_other'
17
+ end
18
+ end
19
+
20
+ attr_reader :current, :bound_to
21
+ end
22
+
23
+ class EmailTakenError < Error
24
+ def message
25
+ 'email_taken'
26
+ end
27
+ end
5
28
  end
6
- end
29
+ end
@@ -0,0 +1,14 @@
1
+ require 'active_record'
2
+ require 'omniauth/multiprovider/config'
3
+
4
+ module OmniAuth
5
+ module MultiProvider
6
+ module ActiveRecord
7
+ def omniauthenticable
8
+ include OmniAuth::MultiProvider::OmniAuthenticable
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ ActiveRecord::Base.extend OmniAuth::MultiProvider::ActiveRecord
@@ -1,7 +1,7 @@
1
1
  require 'hashugar'
2
2
 
3
3
  class Authentication < ActiveRecord::Base
4
- belongs_to OmniAuth::MultiProvider::resource_mapping
4
+ belongs_to :resource, polymorphic: true
5
5
  validates :provider, :uid, presence: true
6
6
 
7
7
  def self.from(omniauth_data, resource)
@@ -24,14 +24,6 @@ class Authentication < ActiveRecord::Base
24
24
  normalized
25
25
  end
26
26
 
27
- def resource
28
- send OmniAuth::MultiProvider::resource_mapping
29
- end
30
-
31
- def resource=(resource)
32
- send("#{OmniAuth::MultiProvider::resource_mapping}=", resource)
33
- end
34
-
35
27
  private
36
28
 
37
29
  def self.find_or_create(auth)
@@ -1,48 +1,95 @@
1
+
1
2
  module OmniAuth
2
3
  module MultiProvider
3
4
 
4
5
  module OmniAuthenticable
5
- extend ActiveSupport::Concern
6
-
7
- def self.authenticate_from_params(params)
8
- resource = nil
9
- if params[:username].present? && params[:password].present?
10
- resource = MultiProvider::resource_klass.find_for_database_authentication(email: params[:username])
11
- resource = nil if resource.nil? || !resource.valid_password?(params[:password])
12
- end
13
- if params[:provider].present?
14
- omniauth_data = {
15
- provider: params[:provider],
16
- uid: params[:user_id],
17
- credentials: {
18
- token: params[:access_token]
19
- }
20
- }
21
- provider_class = "OmniAuth::Provider::#{params[:provider].camelize}".constantize
22
- resource = provider_class.authenticate_from_oauth(params[:provider], omniauth_data)
23
- end
24
- resource
25
- end
6
+ extend ::ActiveSupport::Concern
26
7
 
27
8
  included do
28
- include MultiProvider::EmailMockups
9
+ include OmniAuth::MultiProvider::EmailMockups
10
+
11
+ class << self
12
+ def _oamp
13
+ @_omniauth_multiprovider_config ||= OmniAuth::MultiProvider::Config.new
14
+ end
15
+
16
+ def from_oauth(omniauth_data, signed_in_resource=nil)
17
+ auth = _oamp.authentication_klass.normalize(omniauth_data)
18
+ provider_name = auth.provider
19
+ access_token = auth.credentials.token
20
+ authentication = _oamp.authentication_klass.find_by(provider: provider_name, uid: auth.uid)
21
+
22
+ resource = authentication.try :resource
29
23
 
30
- has_many MultiProvider::authentication_relationship_name, dependent: :destroy, inverse_of: MultiProvider::resource_mapping, autosave: true, class_name: MultiProvider::authentication_klass.name do
31
- def [](provider)
32
- find_by(provider: provider)
24
+ if resource and signed_in_resource
25
+ signed_in_resource.oauth_already_bound resource, auth
26
+ return signed_in_resource
27
+ end
28
+ unless resource
29
+ attributes = oauth_to_attributes auth
30
+ if signed_in_resource
31
+ signed_in_resource.update_from_oauth attributes, auth
32
+ resource = signed_in_resource
33
+ else
34
+ resource = create_from_oauth attributes, auth
35
+ end
36
+ _oamp.authentication_klass.from(auth, resource)
37
+ end
38
+ resource
39
+ end
40
+
41
+ # Can be customized in each model
42
+ def oauth_to_attributes(oauth_data)
43
+ attrs = {
44
+ email: oauth_data.info[:email] || mock_email(oauth_data.provider, oauth_data.uid),
45
+ password: Devise.friendly_token[0,20]
46
+ }
47
+ attrs[:password_confirmation] = attrs[:password]
48
+ return attrs
49
+ end
50
+
51
+ # Can be customized in each model
52
+ def create_from_oauth(attributes, signed_in_resource = nil, oauth_data)
53
+ raise OmniAuth::MultiProvider::EmailTakenError if exists?(email: attributes[:email])
54
+ create!(attributes)
33
55
  end
34
56
  end
35
57
 
36
- omniauth_providers.each do |provider_name|
37
- begin
38
- klass = "OmniAuth::Provider::#{provider_name.to_s.camelize}".constantize
39
- rescue NameError
40
- klass = OmniAuth::Provider::Generic
58
+ has_many _oamp.authentication_relationship_name,
59
+ dependent: :destroy,
60
+ inverse_of: :resource,
61
+ as: :resource,
62
+ autosave: true,
63
+ class_name: _oamp.authentication_klass.name do
64
+ def [](provider)
65
+ find_by(provider: provider)
66
+ end
41
67
  end
42
- klass.init(provider_name)
68
+ end
69
+
70
+ # Handler for the case when there is already an Authentication
71
+ # Can be customized in each model
72
+ def oauth_already_bound(other, oauth_data)
73
+ if self == other
74
+ # update
75
+ self.update_from_oauth self.class.oauth_to_attributes(oauth_data), oauth_data
76
+ else
77
+ # raise error if the oauth data is bound to another resource
78
+ raise MultiProvider::AlreadyBoundError.new self, other
43
79
  end
80
+ end
44
81
 
82
+ # Handler for the case when a signed_in_resource exists and is being authenticated
83
+ # Can be customized in each model
84
+ def update_from_oauth(new_attrs, oauth)
85
+ unless self.email.blank? || new_attrs[:email] == self.email
86
+ # refusing to change existing email
87
+ new_attrs.delete(:email)
88
+ # but check if someone else is using it anyway
89
+ raise OmniAuth::MultiProvider::EmailTakenError if User.exists?(email: new_attrs[:email])
90
+ end
91
+ self.update!(new_attrs)
45
92
  end
46
93
  end
47
94
  end
48
- end
95
+ end