omniauth-multiprovider 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|