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.
- checksums.yaml +4 -4
- data/.gitignore +13 -18
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +52 -7
- data/Rakefile +2 -0
- data/lib/omniauth-multiprovider.rb +3 -1
- data/lib/omniauth/multiprovider.rb +0 -1
- data/lib/omniauth/multiprovider/config.rb +25 -0
- data/lib/omniauth/multiprovider/controllers/callbacks_controller.rb +43 -3
- data/lib/omniauth/multiprovider/error.rb +24 -1
- data/lib/omniauth/multiprovider/models/active_record.rb +14 -0
- data/lib/omniauth/multiprovider/models/authentication.rb +1 -9
- data/lib/omniauth/multiprovider/models/concerns/omni_authenticable.rb +79 -32
- data/lib/omniauth/multiprovider/version.rb +1 -1
- data/lib/omniauth/provider/abstract.rb +6 -38
- data/lib/omniauth/provider/generic.rb +10 -13
- data/omniauth-multiprovider.gemspec +15 -2
- data/spec/omniauth/error_spec.rb +30 -0
- data/spec/omniauth/multiprovider/controllers/callbacks_controller_spec.rb +90 -0
- data/spec/omniauth/multiprovider/models/active_record_spec.rb +21 -0
- data/spec/omniauth/multiprovider/models/concerns/omni_authenticable_spec.rb +67 -0
- data/spec/omniauth/provider/abstract_spec.rb +15 -0
- data/spec/omniauth/provider/facebook_spec.rb +13 -0
- data/spec/omniauth/provider/generic_spec.rb +13 -0
- data/spec/rails_app/.gitignore +17 -0
- data/spec/rails_app/Rakefile +6 -0
- data/spec/rails_app/app/assets/images/.keep +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +16 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +15 -0
- data/spec/rails_app/app/controllers/application_controller.rb +5 -0
- data/spec/rails_app/app/controllers/concerns/.keep +0 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.keep +0 -0
- data/spec/rails_app/app/models/.keep +0 -0
- data/spec/rails_app/app/models/concerns/.keep +0 -0
- data/spec/rails_app/app/models/user.rb +4 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/bin/bundle +3 -0
- data/spec/rails_app/bin/rails +4 -0
- data/spec/rails_app/bin/rake +4 -0
- data/spec/rails_app/bin/setup +29 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +26 -0
- data/spec/rails_app/config/boot.rb +3 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/test.rb +42 -0
- data/spec/rails_app/config/initializers/assets.rb +11 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails_app/config/initializers/devise.rb +259 -0
- data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_app/config/initializers/inflections.rb +16 -0
- data/spec/rails_app/config/initializers/mime_types.rb +4 -0
- data/spec/rails_app/config/initializers/session_store.rb +3 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/en.yml +23 -0
- data/spec/rails_app/config/routes.rb +3 -0
- data/spec/rails_app/config/secrets.yml +22 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/lib/assets/.keep +0 -0
- data/spec/rails_app/lib/tasks/.keep +0 -0
- data/spec/rails_app/public/404.html +67 -0
- data/spec/rails_app/public/422.html +67 -0
- data/spec/rails_app/public/500.html +66 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/shared_examples_for_providers.rb +27 -0
- metadata +226 -7
- data/lib/omniauth/multiprovider/helpers.rb +0 -23
- data/lib/omniauth/provider/guests.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1f3a5a4a52bb8284020af1e7399416e1b1b34d6
|
4
|
+
data.tar.gz: 15ec2a53af0a3a883291ad7c0c8dcb3772c19bef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cda70641cc3aea4b3998bb02e13b22cf9bb0e77dd7bf459d4bb9496841392489be746885a0a402f1a2d1d5da5b5fdf3a86b186b3cc0d792ddca611c90d65789a
|
7
|
+
data.tar.gz: 4ba92b422f566f0b1f7e2e3c88578c05a3e51b939ed2469c6616f28e01f10ee56e2ec1db0d02738c3f8e2c2d95a90dd95ef1fb1a9becf8789322db1ce65b40f5
|
data/.gitignore
CHANGED
@@ -1,19 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
.
|
4
|
-
|
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
|
-
/
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
*.bundle
|
11
|
+
*.so
|
12
|
+
*.o
|
13
|
+
*.a
|
14
|
+
mkmf.log
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
omniauth-multiprovider
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.5
|
data/Gemfile
ADDED
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -1,17 +1,33 @@
|
|
1
|
-
|
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
|
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
|
-
##
|
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
|
-
|
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
|
data/Rakefile
ADDED
@@ -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
|
+
|
@@ -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
|
-
|
5
|
-
|
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
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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
|