svenfuchs-devise_oauth2_authenticatable 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +145 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/devise_oauth2_authenticatable.gemspec +74 -0
- data/lib/devise_oauth2_authenticatable.rb +65 -0
- data/lib/devise_oauth2_authenticatable/locales/en.yml +9 -0
- data/lib/devise_oauth2_authenticatable/model.rb +196 -0
- data/lib/devise_oauth2_authenticatable/rails.rb +24 -0
- data/lib/devise_oauth2_authenticatable/routes.rb +7 -0
- data/lib/devise_oauth2_authenticatable/schema.rb +22 -0
- data/lib/devise_oauth2_authenticatable/strategy.rb +100 -0
- data/lib/devise_oauth2_authenticatable/view_helpers.rb +29 -0
- data/lib/generators/devise/oauth2_authenticatable/USAGE +8 -0
- data/lib/generators/devise/oauth2_authenticatable/oauth2_authenticatable_generator.rb +17 -0
- data/lib/generators/devise/oauth2_authenticatable/templates/oauth2.yml +24 -0
- data/spec/devise_oauth2_authenticatable_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +148 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 bhbryant
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
= devise_oauth2_authenticatable
|
2
|
+
|
3
|
+
== This fork works with Devise 1.1 + Rails 3
|
4
|
+
|
5
|
+
|
6
|
+
==Quick tutorial for Devise 1.1 + Rails 3 + devise_oauth2_authenticatable
|
7
|
+
|
8
|
+
Set up your Facebook app at http://developers.facebook.com/setup/
|
9
|
+
|
10
|
+
Create the Rails app:
|
11
|
+
rails new YOUR_APP
|
12
|
+
cd YOUR_APP
|
13
|
+
|
14
|
+
Add the following lines to your Gemfile:
|
15
|
+
gem "oauth2"
|
16
|
+
gem "devise", :git => "git://github.com/plataformatec/devise.git"
|
17
|
+
gem "devise_oauth2_authenticatable", :git => "git://github.com/jerryluk/devise_oauth2_authenticatable.git"
|
18
|
+
|
19
|
+
Run the following commands:
|
20
|
+
bundle install
|
21
|
+
rails g devise:install
|
22
|
+
rails g devise User
|
23
|
+
rails g devise:oauth2_authenticatable APP_ID SECRET
|
24
|
+
|
25
|
+
Your DeviseCreateUsers migration should look like this:
|
26
|
+
class DeviseCreateUsers < ActiveRecord::Migration
|
27
|
+
def self.up
|
28
|
+
create_table(:users) do |t|
|
29
|
+
t.database_authenticatable
|
30
|
+
t.rememberable
|
31
|
+
t.trackable
|
32
|
+
t.oauth2_authenticatable
|
33
|
+
t.string :email
|
34
|
+
|
35
|
+
t.timestamps
|
36
|
+
end
|
37
|
+
|
38
|
+
add_index :users, :oauth2_uid, :unique => true
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.down
|
42
|
+
drop_table :users
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Make sure user.rb has the following line:
|
47
|
+
devise :oauth2_authenticatable, ...
|
48
|
+
|
49
|
+
Add this to your application_controller.rb:
|
50
|
+
before_filter :authenticate_user!
|
51
|
+
|
52
|
+
Add the sign in/sign out links to your applications.html.erb:
|
53
|
+
<% if user_signed_in? %>
|
54
|
+
<%= link_to "Sign out", destroy_user_session_path %>
|
55
|
+
<% else %>
|
56
|
+
<%= link_to_oauth2 "Sign In with Facebook" %>
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
The usual stuffs:
|
60
|
+
rm public/index.html
|
61
|
+
rake db:create
|
62
|
+
rake db:migrate
|
63
|
+
rails s
|
64
|
+
|
65
|
+
Navigate your browser to http://localhost:3000, there are many things to fix but you are mostly there!
|
66
|
+
|
67
|
+
==
|
68
|
+
This is the basic framework for an OAuth2 gem for Devise.
|
69
|
+
|
70
|
+
It currently works with FacebookGraph, to get started begin by registering a new application at
|
71
|
+
|
72
|
+
http://developers.facebook.com/setup/
|
73
|
+
|
74
|
+
|
75
|
+
A generator is provided for creating your oauth yml file
|
76
|
+
|
77
|
+
rails g devise:oauth2_authenticatable
|
78
|
+
|
79
|
+
Ex:
|
80
|
+
|
81
|
+
rails g devise:oauth2_authenticatable APP_ID SECRET 'email,offline_access,publish_stream'
|
82
|
+
|
83
|
+
|
84
|
+
for more details
|
85
|
+
|
86
|
+
http://developers.facebook.com/docs/authentication/
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
It's based on the devise facebook gem provided by grimen
|
91
|
+
|
92
|
+
http://github.com/grimen/devise_facebook_connectable
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
And uses the example provided in OAuth2 library provided by mbleigh
|
97
|
+
|
98
|
+
http://github.com/intridea/oauth2
|
99
|
+
|
100
|
+
|
101
|
+
DB Migration :
|
102
|
+
|
103
|
+
add_column :users, :oauth2_uid, :integer, :limit => 8 # BIGINT unsigned / 64-bit int
|
104
|
+
add_column :users, :oauth2_token, :string, :limit => 149 # [128][1][20] chars
|
105
|
+
add_index :users, :oauth2_uid, :unique => true
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
Note:
|
110
|
+
|
111
|
+
A little souce of confusion when working with Facebook Graph
|
112
|
+
|
113
|
+
The api key and secret key are no the same a Facebook Connect/the old API.
|
114
|
+
|
115
|
+
The client id should be your application id and the client_key, should be your API key (not secret key)
|
116
|
+
|
117
|
+
|
118
|
+
== TODO
|
119
|
+
|
120
|
+
Write tests :
|
121
|
+
Currently no tests have been written. My bad.
|
122
|
+
|
123
|
+
Add Javascript / token based auth :
|
124
|
+
Facebook graph offes a complete authorization solution using javascript and a returned authentication token. Adding optional support for this would complete the Facebook Graph authentication interface.
|
125
|
+
|
126
|
+
Generalize for OAuth2:
|
127
|
+
Add support for other OAuth2 services. Wrote this specifically for facebook graph, althought configuration arugments should be generalized to support other interfaces.
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
Description goes here.
|
132
|
+
|
133
|
+
== Note on Patches/Pull Requests
|
134
|
+
|
135
|
+
* Fork the project.
|
136
|
+
* Make your feature addition or bug fix.
|
137
|
+
* Add tests for it. This is important so I don't break it in a
|
138
|
+
future version unintentionally.
|
139
|
+
* Commit, do not mess with rakefile, version, or history.
|
140
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
141
|
+
* Send me a pull request. Bonus points for topic branches.
|
142
|
+
|
143
|
+
== Copyright
|
144
|
+
|
145
|
+
Copyright (c) 2010 bhbryant. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "devise_oauth2_authenticatable"
|
8
|
+
gem.summary = %Q{Implements OAuth2 for Devise }
|
9
|
+
gem.description = %Q{Implements OAuth2 for Devise, specifically integrating with facebook Graph}
|
10
|
+
gem.email = "benjamin@bryantmarkowsky.com"
|
11
|
+
gem.homepage = "http://github.com/bhbryant/devise_oauth2_authenticatable"
|
12
|
+
gem.authors = ["bhbryant"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
|
15
|
+
gem.add_dependency'devise', '>= 1.0.0'
|
16
|
+
gem.add_dependency "oauth2"
|
17
|
+
gem.add_dependency "json"
|
18
|
+
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'spec/rake/spectask'
|
27
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
30
|
+
end
|
31
|
+
|
32
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
33
|
+
spec.libs << 'lib' << 'spec'
|
34
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
+
spec.rcov = true
|
36
|
+
end
|
37
|
+
|
38
|
+
task :spec => :check_dependencies
|
39
|
+
|
40
|
+
task :default => :spec
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
45
|
+
|
46
|
+
rdoc.rdoc_dir = 'rdoc'
|
47
|
+
rdoc.title = "devise_oauth2_authenticatable #{version}"
|
48
|
+
rdoc.rdoc_files.include('README*')
|
49
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
50
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{svenfuchs-devise_oauth2_authenticatable}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["bhbryant"]
|
12
|
+
s.date = %q{2010-06-13}
|
13
|
+
s.description = %q{Implements OAuth2 for Devise}
|
14
|
+
s.email = %q{benjamin@bryantmarkowsky.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"devise_oauth2_authenticatable.gemspec",
|
27
|
+
"lib/devise_oauth2_authenticatable.rb",
|
28
|
+
"lib/devise_oauth2_authenticatable/locales/en.yml",
|
29
|
+
"lib/devise_oauth2_authenticatable/rails.rb",
|
30
|
+
"lib/devise_oauth2_authenticatable/model.rb",
|
31
|
+
"lib/devise_oauth2_authenticatable/routes.rb",
|
32
|
+
"lib/devise_oauth2_authenticatable/schema.rb",
|
33
|
+
"lib/devise_oauth2_authenticatable/strategy.rb",
|
34
|
+
"lib/devise_oauth2_authenticatable/view_helpers.rb",
|
35
|
+
"lib/generators/devise/oauth2_authenticatable/oauth2_authenticatable_generator.rb",
|
36
|
+
"lib/generators/devise/oauth2_authenticatable/templates/oauth2.yml",
|
37
|
+
"lib/generators/devise/oauth2_authenticatable/USAGE",
|
38
|
+
"spec/devise_oauth2_authenticatable_spec.rb",
|
39
|
+
"spec/spec.opts",
|
40
|
+
"spec/spec_helper.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/bhbryant/devise_oauth2_authenticatable}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.6}
|
46
|
+
s.summary = %q{Implements OAuth2 for Devise}
|
47
|
+
s.test_files = [
|
48
|
+
"spec/devise_oauth2_authenticatable_spec.rb",
|
49
|
+
"spec/spec_helper.rb"
|
50
|
+
]
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
58
|
+
s.add_runtime_dependency(%q<devise>, [">= 1.0.0"])
|
59
|
+
s.add_runtime_dependency(%q<oauth2>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
61
|
+
else
|
62
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
63
|
+
s.add_dependency(%q<devise>, [">= 1.0.0"])
|
64
|
+
s.add_dependency(%q<oauth2>, [">= 0"])
|
65
|
+
s.add_dependency(%q<json>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
69
|
+
s.add_dependency(%q<devise>, [">= 1.0.0"])
|
70
|
+
s.add_dependency(%q<oauth2>, [">= 0"])
|
71
|
+
s.add_dependency(%q<json>, [">= 0"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'devise'
|
3
|
+
require 'oauth2'
|
4
|
+
|
5
|
+
require 'devise_oauth2_authenticatable/model'
|
6
|
+
require 'devise_oauth2_authenticatable/strategy'
|
7
|
+
require 'devise_oauth2_authenticatable/schema'
|
8
|
+
require 'devise_oauth2_authenticatable/routes'
|
9
|
+
#require 'devise_oauth2_authenticatable/controller_filters'
|
10
|
+
require 'devise_oauth2_authenticatable/view_helpers'
|
11
|
+
require 'devise_oauth2_authenticatable/rails'
|
12
|
+
|
13
|
+
module Devise
|
14
|
+
# Specifies the name of the database column name used for storing
|
15
|
+
# the oauth UID. Useful if this info should be saved in a
|
16
|
+
# generic column if different authentication solutions are used.
|
17
|
+
mattr_accessor :oauth2_uid_field
|
18
|
+
@@oauth2_uid_field = :oauth2_uid
|
19
|
+
|
20
|
+
# Specifies the name of the database column name used for storing
|
21
|
+
# the user Facebook session key. Useful if this info should be saved in a
|
22
|
+
# generic column if different authentication solutions are used.
|
23
|
+
mattr_accessor :oauth2_token_field
|
24
|
+
@@oauth2_token_field = :oauth2_token
|
25
|
+
|
26
|
+
# Specifies if account should be created if no account exists for
|
27
|
+
# a specified Facebook UID or not.
|
28
|
+
mattr_accessor :oauth2_auto_create_account
|
29
|
+
@@oauth2_auto_create_account = true
|
30
|
+
|
31
|
+
def self.oauth2_client
|
32
|
+
@@oauth2_client ||= OAuth2::Client.new(
|
33
|
+
OAUTH2_CONFIG['client_id'],
|
34
|
+
OAUTH2_CONFIG['client_secret'],
|
35
|
+
:site => OAUTH2_CONFIG['authorization_server'],
|
36
|
+
:authorize_path => OAUTH2_CONFIG['authorize_path'],
|
37
|
+
:access_token_path => OAUTH2_CONFIG['access_token_path'])
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def self.session_sign_in_url(request, mapping)
|
42
|
+
url = URI.parse(request.url)
|
43
|
+
# url.path = "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
|
44
|
+
url.path = "/#{mapping.path}/#{mapping.path_names[:oauth2]}"
|
45
|
+
url.query = nil
|
46
|
+
url.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.requested_scope
|
50
|
+
@@requested_scope ||= OAUTH2_CONFIG['requested_scope']
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Load core I18n locales: en
|
56
|
+
#
|
57
|
+
I18n.load_path.unshift File.join(File.dirname(__FILE__), *%w[devise_oauth2_authenticatable locales en.yml])
|
58
|
+
|
59
|
+
# Add +:facebook_connectable+ strategies to defaults.
|
60
|
+
#
|
61
|
+
Devise.add_module(:oauth2_authenticatable,
|
62
|
+
:strategy => true,
|
63
|
+
:controller => :sessions,
|
64
|
+
:route => :oauth2,
|
65
|
+
:model => 'devise_oauth2_authenticatable/model')
|
@@ -0,0 +1,9 @@
|
|
1
|
+
en:
|
2
|
+
devise:
|
3
|
+
sessions:
|
4
|
+
oauth2_invalid: "Could not login. Invalid account."
|
5
|
+
oauth2_timeout: "OAuth2 session expired., please sign in again to continue."
|
6
|
+
oauth2_authenticity_token: "Something went wrong. For security reasons, please sign in again." # Revise this message =)
|
7
|
+
oauth2_actions:
|
8
|
+
sign_in: "Sign in" # NOTE: Not used for the default Facebook Connect button.
|
9
|
+
sign_out: "Sign out"
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'devise/models'
|
3
|
+
|
4
|
+
|
5
|
+
module Devise #:nodoc:
|
6
|
+
# module OAuth2Authenticatable #:nodoc:
|
7
|
+
module Models #:nodoc:
|
8
|
+
|
9
|
+
# OAuth2 Connectable Module, responsible for validating authenticity of a
|
10
|
+
# user and storing credentials while signing in using their OAuth2 account.
|
11
|
+
#
|
12
|
+
# == Configuration:
|
13
|
+
#
|
14
|
+
# You can overwrite configuration values by setting in globally in Devise (+Devise.setup+),
|
15
|
+
# using devise method, or overwriting the respective instance method.
|
16
|
+
#
|
17
|
+
# +oauth2_uid_field+ - Defines the name of the OAuth2 user UID database attribute/column.
|
18
|
+
#
|
19
|
+
# +oauth2_token_field+ - Defines the name of the OAuth2 session key database attribute/column.
|
20
|
+
#
|
21
|
+
# +oauth2_auto_create_account+ - Speifies if account should automatically be created upon connect
|
22
|
+
# if not already exists.
|
23
|
+
#
|
24
|
+
# == Examples:
|
25
|
+
#
|
26
|
+
# User.oauth2_connect(:uid => '123456789') # returns authenticated user or nil
|
27
|
+
# User.find(1).oauth2_connected? # returns true/false
|
28
|
+
#
|
29
|
+
module Oauth2Authenticatable
|
30
|
+
|
31
|
+
def self.included(base) #:nodoc:
|
32
|
+
base.class_eval do
|
33
|
+
extend ClassMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Store OAuth2 Connect account/session credentials.
|
38
|
+
#
|
39
|
+
def store_oauth2_credentials!(attributes = {})
|
40
|
+
self.send(:"#{self.class.oauth2_uid_field}=", attributes[:uid])
|
41
|
+
self.send(:"#{self.class.oauth2_token_field}=", attributes[:token])
|
42
|
+
|
43
|
+
# Confirm without e-mail - if confirmable module is loaded.
|
44
|
+
self.skip_confirmation! if self.respond_to?(:skip_confirmation!)
|
45
|
+
|
46
|
+
# Only populate +email+ field if it's available (e.g. if +authenticable+ module is used).
|
47
|
+
self.email = attributes[:email] || '' if self.respond_to?(:email)
|
48
|
+
|
49
|
+
# Lazy hack: These database fields are required if +authenticable+/+confirmable+
|
50
|
+
# module(s) is used. Could be avoided with :null => true for authenticatable
|
51
|
+
# migration, but keeping this to avoid unnecessary problems.
|
52
|
+
self.password_salt = '' if self.respond_to?(:password_salt)
|
53
|
+
self.encrypted_password = '' if self.respond_to?(:encrypted_password)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks if OAuth2 Connected.
|
57
|
+
#
|
58
|
+
def oauth2_connected?
|
59
|
+
self.send(:"#{self.class.oauth2_uid_field}").present?
|
60
|
+
end
|
61
|
+
alias :is_oauth2_connected? :oauth2_connected?
|
62
|
+
|
63
|
+
# Hook that gets called *before* connect (only at creation). Useful for
|
64
|
+
# specifiying additional user info (etc.) from OAuth2.
|
65
|
+
#
|
66
|
+
# Default: Do nothing.
|
67
|
+
#
|
68
|
+
# == Examples:
|
69
|
+
#
|
70
|
+
# # Overridden in OAuth2 Connect:able model, e.g. "User".
|
71
|
+
# #
|
72
|
+
# def before_oauth2_auto_create(oauth2_user_attributes)
|
73
|
+
|
74
|
+
# self.profile.first_name = oauth2_user_attributes.first_name
|
75
|
+
|
76
|
+
#
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# == For more info:
|
80
|
+
#
|
81
|
+
# * http://oauth2er.pjkh.com/user/populate
|
82
|
+
#
|
83
|
+
def on_before_oauth2_auto_create(oauth2_user_attributes)
|
84
|
+
|
85
|
+
if self.respond_to?(:before_oauth2_auto_create)
|
86
|
+
self.send(:before_oauth2_auto_create, oauth2_user_attributes) rescue nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Hook that gets called *after* a connection (each time). Useful for
|
91
|
+
# fetching additional user info (etc.) from OAuth2.
|
92
|
+
#
|
93
|
+
# Default: Do nothing.
|
94
|
+
#
|
95
|
+
# == Example:
|
96
|
+
#
|
97
|
+
# # Overridden in OAuth2 Connect:able model, e.g. "User".
|
98
|
+
# #
|
99
|
+
# def after_oauth2_connect(oauth2_user_attributes)
|
100
|
+
# # See "on_before_oauth2_connect" example.
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
def on_after_oauth2_connect(oauth2_user_attributes)
|
104
|
+
|
105
|
+
if self.respond_to?(:after_oauth2_connect)
|
106
|
+
self.send(:after_oauth2_connect, oauth2_user_attributes) rescue nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Optional: Store session key.
|
111
|
+
#
|
112
|
+
def store_session(using_token)
|
113
|
+
if self.token != using_token
|
114
|
+
self.update_attribute(self.send(:"#{self.class.oauth2_token_field}"), using_token)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
# Passwords are always required if it's a new rechord and no oauth_id exists, or if the password
|
121
|
+
# or confirmation are being set somewhere.
|
122
|
+
def password_required?
|
123
|
+
|
124
|
+
( new_record? && self.send(:"#{self.class.oauth2_uid_field}").nil? ) || !password.nil? || !password_confirmation.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
module ClassMethods
|
128
|
+
|
129
|
+
# Configuration params accessible within +Devise.setup+ procedure (in initalizer).
|
130
|
+
#
|
131
|
+
# == Example:
|
132
|
+
#
|
133
|
+
# Devise.setup do |config|
|
134
|
+
# config.oauth2_uid_field = :oauth2_uid
|
135
|
+
# config.oauth2_token_field = :oauth2_token
|
136
|
+
# config.oauth2_auto_create_account = true
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
::Devise::Models.config(self,
|
140
|
+
:oauth2_uid_field,
|
141
|
+
:oauth2_token_field,
|
142
|
+
:oauth2_auto_create_account
|
143
|
+
)
|
144
|
+
|
145
|
+
# Alias don't work for some reason, so...a more Ruby-ish alias
|
146
|
+
# for +oauth2_auto_create_account+.
|
147
|
+
#
|
148
|
+
def oauth2_auto_create_account?
|
149
|
+
self.oauth2_auto_create_account
|
150
|
+
end
|
151
|
+
|
152
|
+
# Authenticate a user based on OAuth2 UID.
|
153
|
+
#
|
154
|
+
def authenticate_with_oauth2(oauth2_id, oauth2_token)
|
155
|
+
|
156
|
+
# find user and update access token
|
157
|
+
returning(self.find_for_oauth2(oauth2_id)) do |user|
|
158
|
+
user.update_attributes(:"#{self.oauth2_token_field}" => oauth2_token) unless user.nil?
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
# Find first record based on conditions given (OAuth2 UID).
|
168
|
+
# Overwrite to add customized conditions, create a join, or maybe use a
|
169
|
+
# namedscope to filter records while authenticating.
|
170
|
+
#
|
171
|
+
# == Example:
|
172
|
+
#
|
173
|
+
# def self.find_for_oauth2(uid, conditions = {})
|
174
|
+
# conditions[:active] = true
|
175
|
+
# self.find_by_oauth2_uid(uid, :conditions => conditions)
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
def find_for_oauth2(uid, conditions = {})
|
179
|
+
self.send(:"find_by_#{self.oauth2_uid_field}",uid, :conditions => conditions)
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
# Contains the logic used in authentication. Overwritten by other devise modules.
|
185
|
+
# In the OAuth2 Connect case; nothing fancy required.
|
186
|
+
#
|
187
|
+
def valid_for_oauth2(resource, attributes)
|
188
|
+
true
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# end
|
196
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Devise
|
2
|
+
class Engine
|
3
|
+
config.after_initialize do
|
4
|
+
begin
|
5
|
+
config_file = Rails.root.join('config', 'oauth2.yml')
|
6
|
+
unless File.exists?(config_file)
|
7
|
+
config_file = Rails.root.join('config', 'oauth2_config.yml')
|
8
|
+
if File.exists?(config_file)
|
9
|
+
puts "Deprecated OAuth2 config file config/oauth2_config.yml. Please use config/oauth.yml instead."
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Devise::OAUTH2_CONFIG = YAML.load_file(config_file)[Rails.env]
|
14
|
+
|
15
|
+
unless Devise::OAUTH2_CONFIG['user_attributes_path']
|
16
|
+
puts "Your config/oauth2.yml does not seem to contain a user_attributes_path setting. Assuming '/me' for FacebookConnect."
|
17
|
+
Devise::OAUTH2_CONFIG['user_attributes_path'] = '/me'
|
18
|
+
end
|
19
|
+
rescue
|
20
|
+
puts "Can't load oauth2.yml. You can generate with rails g devise:oauth2_authenticatable"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'devise/schema'
|
3
|
+
|
4
|
+
module Devise #:nodoc:
|
5
|
+
module Oauth2Authenticatable #:nodoc:
|
6
|
+
|
7
|
+
module Schema
|
8
|
+
|
9
|
+
# Database migration schema for Facebook Connect.
|
10
|
+
#
|
11
|
+
def oauth2_authenticatable
|
12
|
+
apply_devise_schema ::Devise.oauth2_uid_field, Integer, :limit => 8 # BIGINT unsigned / 64-bit int
|
13
|
+
apply_devise_schema ::Devise.oauth2_token_field, String, :limit => 149 # [128][1][20] chars
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Devise::Schema.module_eval do
|
21
|
+
include ::Devise::Oauth2Authenticatable::Schema
|
22
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'devise/strategies/base'
|
3
|
+
|
4
|
+
|
5
|
+
module Devise #:nodoc:
|
6
|
+
module Oauth2Authenticatable #:nodoc:
|
7
|
+
module Strategies #:nodoc:
|
8
|
+
|
9
|
+
# Default strategy for signing in a user using Facebook Connect (a Facebook account).
|
10
|
+
# Redirects to sign_in page if it's not authenticated
|
11
|
+
#
|
12
|
+
class Oauth2Authenticatable < ::Devise::Strategies::Base
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
# Without a oauth session authentication cannot proceed.
|
17
|
+
#
|
18
|
+
def valid?
|
19
|
+
|
20
|
+
valid_controller? && valid_params? && mapping.to.respond_to?('authenticate_with_oauth2')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Authenticate user with OAuth2
|
25
|
+
#
|
26
|
+
def authenticate!
|
27
|
+
klass = mapping.to
|
28
|
+
begin
|
29
|
+
|
30
|
+
|
31
|
+
# Verify User Auth code and get access token from auth server: will error on failue
|
32
|
+
access_token = Devise::oauth2_client.web_server.get_access_token(
|
33
|
+
params[:code], :redirect_uri => Devise::session_sign_in_url(request,mapping)
|
34
|
+
)
|
35
|
+
|
36
|
+
# retrieve user attributes
|
37
|
+
|
38
|
+
# Get user details from OAuth2 Service
|
39
|
+
oauth2_user_attributes = JSON.parse(access_token.get(OAUTH2_CONFIG['user_attributes_path']))
|
40
|
+
|
41
|
+
user = klass.authenticate_with_oauth2(oauth2_user_attributes['id'], access_token.token)
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
if user.present?
|
46
|
+
user.on_after_oauth2_connect(oauth2_user_attributes)
|
47
|
+
success!(user)
|
48
|
+
else
|
49
|
+
if klass.oauth2_auto_create_account?
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
user = returning(klass.new) do |u|
|
54
|
+
u.store_oauth2_credentials!(
|
55
|
+
:token => access_token.token,
|
56
|
+
:uid => oauth2_user_attributes['id']
|
57
|
+
)
|
58
|
+
u.on_before_oauth2_auto_create(oauth2_user_attributes)
|
59
|
+
end
|
60
|
+
|
61
|
+
begin
|
62
|
+
|
63
|
+
|
64
|
+
user.save(true)
|
65
|
+
user.on_after_oauth2_connect(oauth2_user_attributes)
|
66
|
+
|
67
|
+
|
68
|
+
success!(user)
|
69
|
+
rescue
|
70
|
+
fail!(:oauth2_invalid)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
fail!(:oauth2_invalid)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
rescue => e
|
78
|
+
fail!(e.message)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
protected
|
86
|
+
def valid_controller?
|
87
|
+
# params[:controller] == 'sessions'
|
88
|
+
mapping.controllers[:sessions] == params[:controller]
|
89
|
+
end
|
90
|
+
|
91
|
+
def valid_params?
|
92
|
+
params[:code].present?
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Warden::Strategies.add(:oauth2_authenticatable, Devise::Oauth2Authenticatable::Strategies::Oauth2Authenticatable)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'devise/mapping'
|
3
|
+
|
4
|
+
module Devise #:nodoc:
|
5
|
+
module Oauth2Authenticatable #:nodoc:
|
6
|
+
|
7
|
+
# OAuth2 view helpers to easily add the link to the OAuth2 connection popup and also the necessary JS code.
|
8
|
+
#
|
9
|
+
module Helpers
|
10
|
+
|
11
|
+
# Creates the link to
|
12
|
+
def link_to_oauth2(link_text, options={})
|
13
|
+
|
14
|
+
|
15
|
+
session_sign_in_url = Devise::session_sign_in_url(request,::Devise.mappings[:user])
|
16
|
+
|
17
|
+
link_to link_text, Devise::oauth2_client.web_server.authorize_url(
|
18
|
+
:redirect_uri => session_sign_in_url,
|
19
|
+
:scope => Devise::requested_scope
|
20
|
+
), options
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
::ActionView::Base.send :include, Devise::Oauth2Authenticatable::Helpers
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Devise
|
2
|
+
class Oauth2AuthenticatableGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
argument :client_id, :type => :string, :default => "YOUR_APP_API_ID"
|
5
|
+
argument :client_key, :type => :string, :default => "YOUR_APP_SECRET_KEY"
|
6
|
+
argument :requested_scope, :type => :string, :default => "email,offline_access,publish_stream"
|
7
|
+
argument :auth_server, :type => :string, :default => "https://graph.facebook.com"
|
8
|
+
argument :authorize_path, :type => :string, :default => "/oauth/authorize"
|
9
|
+
argument :access_token_path, :type => :string, :default => "/oauth/access_token"
|
10
|
+
argument :user_attributes_path, :type => :string, :default => "/me"
|
11
|
+
desc "Generates a OAuth2 config file for your application. All the parameters are optional."
|
12
|
+
|
13
|
+
def generate_oauth2_config
|
14
|
+
template 'oauth2_config.yml', File.join(*%w[config oauth2.yml])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
# Required.
|
3
|
+
client_id: <%= client_id %>
|
4
|
+
client_secret: <%= client_key %>
|
5
|
+
authorization_server: <%= auth_server %>
|
6
|
+
requested_scope: <%= requested_scope %>
|
7
|
+
authorize_path: <%= authorize_path %>
|
8
|
+
access_token_path: <%= access_token_path %>
|
9
|
+
user_attributes_path: <%= user_attributes_path %>
|
10
|
+
|
11
|
+
development:
|
12
|
+
<<: *defaults
|
13
|
+
|
14
|
+
test: &test
|
15
|
+
<<: *defaults
|
16
|
+
|
17
|
+
production: &production
|
18
|
+
<<: *defaults
|
19
|
+
|
20
|
+
# staging:
|
21
|
+
# <<: *production
|
22
|
+
#
|
23
|
+
# cucumber:
|
24
|
+
# <<: *test
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: svenfuchs-devise_oauth2_authenticatable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- bhbryant
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-13 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 13
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 2
|
33
|
+
- 9
|
34
|
+
version: 1.2.9
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: devise
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: oauth2
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: json
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :runtime
|
80
|
+
version_requirements: *id004
|
81
|
+
description: Implements OAuth2 for Devise
|
82
|
+
email: benjamin@bryantmarkowsky.com
|
83
|
+
executables: []
|
84
|
+
|
85
|
+
extensions: []
|
86
|
+
|
87
|
+
extra_rdoc_files:
|
88
|
+
- LICENSE
|
89
|
+
- README.rdoc
|
90
|
+
files:
|
91
|
+
- .document
|
92
|
+
- .gitignore
|
93
|
+
- LICENSE
|
94
|
+
- README.rdoc
|
95
|
+
- Rakefile
|
96
|
+
- VERSION
|
97
|
+
- devise_oauth2_authenticatable.gemspec
|
98
|
+
- lib/devise_oauth2_authenticatable.rb
|
99
|
+
- lib/devise_oauth2_authenticatable/locales/en.yml
|
100
|
+
- lib/devise_oauth2_authenticatable/rails.rb
|
101
|
+
- lib/devise_oauth2_authenticatable/model.rb
|
102
|
+
- lib/devise_oauth2_authenticatable/routes.rb
|
103
|
+
- lib/devise_oauth2_authenticatable/schema.rb
|
104
|
+
- lib/devise_oauth2_authenticatable/strategy.rb
|
105
|
+
- lib/devise_oauth2_authenticatable/view_helpers.rb
|
106
|
+
- lib/generators/devise/oauth2_authenticatable/oauth2_authenticatable_generator.rb
|
107
|
+
- lib/generators/devise/oauth2_authenticatable/templates/oauth2.yml
|
108
|
+
- lib/generators/devise/oauth2_authenticatable/USAGE
|
109
|
+
- spec/devise_oauth2_authenticatable_spec.rb
|
110
|
+
- spec/spec.opts
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
has_rdoc: true
|
113
|
+
homepage: http://github.com/bhbryant/devise_oauth2_authenticatable
|
114
|
+
licenses: []
|
115
|
+
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options:
|
118
|
+
- --charset=UTF-8
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
hash: 3
|
136
|
+
segments:
|
137
|
+
- 0
|
138
|
+
version: "0"
|
139
|
+
requirements: []
|
140
|
+
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 1.3.7
|
143
|
+
signing_key:
|
144
|
+
specification_version: 3
|
145
|
+
summary: Implements OAuth2 for Devise
|
146
|
+
test_files:
|
147
|
+
- spec/devise_oauth2_authenticatable_spec.rb
|
148
|
+
- spec/spec_helper.rb
|