devise_oauth2_providable 0.1.6 → 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.
- data/README.md +5 -3
- data/app/models/access_token.rb +8 -15
- data/app/models/client.rb +7 -4
- data/app/views/oauth2/authorizations/new.html.erb +1 -1
- data/lib/devise_oauth2_providable/schema.rb +1 -1
- data/lib/devise_oauth2_providable/version.rb +1 -1
- data/lib/devise_oauth2_providable.rb +5 -2
- data/lib/expirable_token.rb +9 -5
- data/lib/token_endpoint.rb +23 -21
- data/spec/rails_app/db/schema.rb +1 -0
- data/spec/rails_app/spec/integration/token_endpoint_spec.rb +29 -1
- data/spec/rails_app/spec/models/access_token_spec.rb +19 -0
- data/spec/rails_app/spec/models/refresh_token_spec.rb +21 -0
- metadata +6 -4
data/README.md
CHANGED
@@ -62,7 +62,7 @@ requiring user intervention to re-authorize.
|
|
62
62
|
|
63
63
|
expires after 1 month by default.
|
64
64
|
|
65
|
-
###
|
65
|
+
### AuthorizationCode
|
66
66
|
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-1.4.1
|
67
67
|
|
68
68
|
*Very* short lived token created to allow a client to request an access
|
@@ -92,8 +92,10 @@ supported flows.
|
|
92
92
|
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
|
93
93
|
|
94
94
|
in order to use the Resource Owner Password Credentials Grant Type, your
|
95
|
-
Devise model *must* be configured
|
96
|
-
|
95
|
+
Devise model *must* be configured with the :database_authenticatable option
|
96
|
+
|
97
|
+
### Client Credentials Grant Type
|
98
|
+
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
|
97
99
|
|
98
100
|
### Authorization Code Grant Type
|
99
101
|
http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
|
data/app/models/access_token.rb
CHANGED
@@ -3,15 +3,13 @@ require 'expirable_token'
|
|
3
3
|
class AccessToken < ActiveRecord::Base
|
4
4
|
include ExpirableToken
|
5
5
|
self.default_lifetime = 15.minutes
|
6
|
-
attr_accessor :refresh_token
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if
|
14
|
-
refresh_token = client.refresh_tokens.create! :user => self.user
|
7
|
+
before_validation :restrict_expires_at, :if => :refresh_token
|
8
|
+
belongs_to :refresh_token
|
9
|
+
|
10
|
+
def to_bearer_token
|
11
|
+
bearer_token = Rack::OAuth2::AccessToken::Bearer.new :access_token => self.token, :expires_in => self.expires_in
|
12
|
+
if refresh_token
|
15
13
|
bearer_token.refresh_token = refresh_token.token
|
16
14
|
end
|
17
15
|
bearer_token
|
@@ -19,12 +17,7 @@ class AccessToken < ActiveRecord::Base
|
|
19
17
|
|
20
18
|
private
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
if refresh_token
|
25
|
-
self.user = refresh_token.user
|
26
|
-
self.client = refresh_token.client
|
27
|
-
self.expires_at = [self.expires_at, refresh_token.expires_at].min
|
28
|
-
end
|
20
|
+
def restrict_expires_at
|
21
|
+
self.expires_at = [self.expires_at, refresh_token.expires_at].min
|
29
22
|
end
|
30
23
|
end
|
data/app/models/client.rb
CHANGED
@@ -2,14 +2,17 @@ class Client < ActiveRecord::Base
|
|
2
2
|
has_many :access_tokens
|
3
3
|
has_many :refresh_tokens
|
4
4
|
|
5
|
-
before_validation :
|
5
|
+
before_validation :init_identifier, :on => :create, :unless => :identifier?
|
6
|
+
before_validation :init_secret, :on => :create, :unless => :secret?
|
6
7
|
validates :name, :website, :redirect_uri, :secret, :presence => true
|
7
8
|
validates :identifier, :presence => true, :uniqueness => true
|
8
9
|
|
9
10
|
private
|
10
11
|
|
11
|
-
def
|
12
|
-
self.identifier =
|
13
|
-
|
12
|
+
def init_identifier
|
13
|
+
self.identifier = Devise::Oauth2Providable.random_id
|
14
|
+
end
|
15
|
+
def init_secret
|
16
|
+
self.secret = Devise::Oauth2Providable.random_id
|
14
17
|
end
|
15
18
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<h2><%= link_to @client.name, @client.website %> is permission to access your resources.</h2>
|
1
|
+
<h2><%= link_to @client.name, @client.website %> is requesting permission to access your resources.</h2>
|
2
2
|
|
3
3
|
<%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :approve %>
|
4
4
|
<%= render 'oauth2/authorizations/form', :client => @client, :response_type => @response_type, :redirect_uri => @redirect_uri, :action => :deny %>
|
data/lib/expirable_token.rb
CHANGED
@@ -7,8 +7,10 @@ module ExpirableToken
|
|
7
7
|
belongs_to :user
|
8
8
|
belongs_to :client
|
9
9
|
|
10
|
-
before_validation :
|
11
|
-
|
10
|
+
before_validation :init_token, :on => :create, :unless => :token?
|
11
|
+
before_validation :init_expires_at, :on => :create, :unless => :expires_at?
|
12
|
+
validates :expires_at, :presence => true
|
13
|
+
validates :client, :presence => true
|
12
14
|
validates :token, :presence => true, :uniqueness => true
|
13
15
|
|
14
16
|
# TODO: this should be a default scope once rails default_scope supports lambda's
|
@@ -29,9 +31,11 @@ module ExpirableToken
|
|
29
31
|
|
30
32
|
private
|
31
33
|
|
32
|
-
def
|
33
|
-
self.token =
|
34
|
-
|
34
|
+
def init_token
|
35
|
+
self.token = Devise::Oauth2Providable.random_id
|
36
|
+
end
|
37
|
+
def init_expires_at
|
38
|
+
self.expires_at = self.default_lifetime.from_now
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
data/lib/token_endpoint.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class TokenEndpoint
|
2
|
-
|
2
|
+
class InvalidGrantType < StandardError; end
|
3
3
|
def call(env)
|
4
4
|
authenticator.call(env)
|
5
5
|
end
|
@@ -8,41 +8,43 @@ class TokenEndpoint
|
|
8
8
|
|
9
9
|
def authenticator
|
10
10
|
Rack::OAuth2::Server::Token.new do |req, res|
|
11
|
-
client = Client.find_by_identifier(req.client_id)
|
12
|
-
client.secret == req.client_secret
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
res.access_token = token.to_bearer_token include_bearer_token
|
18
|
-
else
|
11
|
+
client = Client.find_by_identifier(req.client_id)
|
12
|
+
req.invalid_client! unless client && client.secret == req.client_secret
|
13
|
+
begin
|
14
|
+
res.access_token = access_token(req, client).to_bearer_token
|
15
|
+
rescue => e
|
16
|
+
puts e.inspect
|
19
17
|
req.invalid_grant!
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
|
-
# NOTE: extended assertion grant_types are not supported yet.
|
25
22
|
def access_token(req, client)
|
23
|
+
refresh_token = find_refresh_token(req, client)
|
24
|
+
refresh_token.access_tokens.create!(:client => client, :user => refresh_token.user)
|
25
|
+
end
|
26
|
+
|
27
|
+
# NOTE: extended assertion grant_types are not supported yet.
|
28
|
+
# NOTE: client_credentials grant_types are not yet supported
|
29
|
+
def find_refresh_token(req, client)
|
26
30
|
case req.grant_type
|
27
31
|
when :authorization_code
|
28
32
|
code = AuthorizationCode.valid.find_by_token(req.code)
|
29
|
-
|
30
|
-
code.
|
33
|
+
raise InvalidGrantType.new('invalid authorization code') unless code && code.valid_request?(req)
|
34
|
+
client.refresh_tokens.create! :user => code.user
|
31
35
|
when :password
|
32
36
|
resource = mapping.to.find_for_authentication(mapping.to.authentication_keys.first => req.username)
|
33
|
-
|
37
|
+
raise InvalidGrantType.new('user not found') unless resource
|
38
|
+
raise InvalidGrantType.new('user does not support password authentication') unless resource.respond_to?(:valid_password?)
|
34
39
|
valid = resource.valid_for_authentication? { resource.valid_password?(req.password) }
|
35
|
-
|
36
|
-
|
37
|
-
when :client_credentials
|
38
|
-
# NOTE: client is already authenticated here.
|
39
|
-
client.access_tokens.build
|
40
|
+
raise InvalidGrantType.new("authentication failed: #{valid}") unless valid.is_a?(TrueClass)
|
41
|
+
client.refresh_tokens.create! :user => resource
|
40
42
|
when :refresh_token
|
41
43
|
refresh_token = client.refresh_tokens.valid.find_by_token(req.refresh_token)
|
42
|
-
|
43
|
-
refresh_token
|
44
|
+
raise InvalidGrantType.new('refresh token not found') unless refresh_token
|
45
|
+
refresh_token
|
44
46
|
else
|
45
|
-
|
47
|
+
raise InvalidGrantType.new('invalid grant type')
|
46
48
|
end
|
47
49
|
end
|
48
50
|
def mapping
|
data/spec/rails_app/db/schema.rb
CHANGED
@@ -15,6 +15,7 @@ ActiveRecord::Schema.define(:version => 20110511210926) do
|
|
15
15
|
create_table "access_tokens", :force => true do |t|
|
16
16
|
t.integer "user_id"
|
17
17
|
t.integer "client_id"
|
18
|
+
t.integer "refresh_token_id"
|
18
19
|
t.string "token"
|
19
20
|
t.datetime "expires_at"
|
20
21
|
t.datetime "created_at"
|
@@ -1,6 +1,35 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe TokenEndpoint do
|
4
|
+
describe 'refresh_token grant type' do
|
5
|
+
context 'with valid params' do
|
6
|
+
before do
|
7
|
+
@user = User.create! :email => 'ryan@socialcast.com', :name => 'ryan sonnek', :password => 'test'
|
8
|
+
@client = Client.create! :name => 'example', :redirect_uri => 'http://localhost', :website => 'http://localhost'
|
9
|
+
@refresh_token = @client.refresh_tokens.create! :user => @user
|
10
|
+
params = {
|
11
|
+
:grant_type => 'refresh_token',
|
12
|
+
:client_id => @client.identifier,
|
13
|
+
:client_secret => @client.secret,
|
14
|
+
:refresh_token => @refresh_token.token
|
15
|
+
}
|
16
|
+
|
17
|
+
post '/oauth2/token', params
|
18
|
+
end
|
19
|
+
it { response.code.to_i.should == 200 }
|
20
|
+
it 'returns json' do
|
21
|
+
token = AccessToken.last
|
22
|
+
refresh_token = @refresh_token
|
23
|
+
expected = {
|
24
|
+
:token_type => 'bearer',
|
25
|
+
:expires_in => 899,
|
26
|
+
:refresh_token => refresh_token.token,
|
27
|
+
:access_token => token.token
|
28
|
+
}
|
29
|
+
response.body.should == expected.to_json
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
4
33
|
describe 'password grant type' do
|
5
34
|
context 'with valid params' do
|
6
35
|
before do
|
@@ -30,7 +59,6 @@ describe TokenEndpoint do
|
|
30
59
|
response.body.should == expected.to_json
|
31
60
|
end
|
32
61
|
end
|
33
|
-
|
34
62
|
context 'with invalid params' do
|
35
63
|
before do
|
36
64
|
@user = User.create! :email => 'ryan@socialcast.com', :name => 'ryan sonnek', :password => 'test'
|
@@ -12,6 +12,25 @@ describe AccessToken do
|
|
12
12
|
it { should belong_to :client }
|
13
13
|
it { should validate_presence_of :client }
|
14
14
|
it { should validate_presence_of :expires_at }
|
15
|
+
it { should belong_to :refresh_token }
|
16
|
+
it { should allow_mass_assignment_of :refresh_token }
|
17
|
+
it { should have_db_index :client_id }
|
18
|
+
it { should have_db_index :user_id }
|
19
|
+
it { should have_db_index :token }
|
20
|
+
it { should have_db_index :expires_at }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'refresh token expires before access token expires_at' do
|
24
|
+
before do
|
25
|
+
@soon = 1.minute.from_now
|
26
|
+
client = Client.create! :name => 'test', :redirect_uri => 'http://localhost:3000', :website => 'http://localhost'
|
27
|
+
@refresh_token = client.refresh_tokens.create!
|
28
|
+
@refresh_token.expires_at = @soon
|
29
|
+
@access_token = AccessToken.create! :client => client, :refresh_token => @refresh_token
|
30
|
+
end
|
31
|
+
it 'should set the access token expires_at to equal refresh token' do
|
32
|
+
@access_token.expires_at.should eq @soon
|
33
|
+
end
|
15
34
|
end
|
16
35
|
end
|
17
36
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RefreshToken do
|
4
|
+
describe 'basic refresh token instance' do
|
5
|
+
subject do
|
6
|
+
client = Client.create! :name => 'test', :redirect_uri => 'http://localhost:3000', :website => 'http://localhost'
|
7
|
+
RefreshToken.create! :client => client
|
8
|
+
end
|
9
|
+
it { should validate_presence_of :token }
|
10
|
+
it { should validate_uniqueness_of :token }
|
11
|
+
it { should belong_to :user }
|
12
|
+
it { should belong_to :client }
|
13
|
+
it { should validate_presence_of :client }
|
14
|
+
it { should validate_presence_of :expires_at }
|
15
|
+
it { should have_many :access_tokens }
|
16
|
+
it { should have_db_index :client_id }
|
17
|
+
it { should have_db_index :user_id }
|
18
|
+
it { should have_db_index :token }
|
19
|
+
it { should have_db_index :expires_at }
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Sonnek
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-16 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rails
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- spec/rails_app/spec/integration/token_endpoint_spec.rb
|
164
164
|
- spec/rails_app/spec/models/access_token_spec.rb
|
165
165
|
- spec/rails_app/spec/models/client_spec.rb
|
166
|
+
- spec/rails_app/spec/models/refresh_token_spec.rb
|
166
167
|
- spec/rails_app/spec/models/user_spec.rb
|
167
168
|
- spec/rails_app/spec/spec_helper.rb
|
168
169
|
- spec/rails_app/vendor/plugins/.gitkeep
|
@@ -249,6 +250,7 @@ test_files:
|
|
249
250
|
- spec/rails_app/spec/integration/token_endpoint_spec.rb
|
250
251
|
- spec/rails_app/spec/models/access_token_spec.rb
|
251
252
|
- spec/rails_app/spec/models/client_spec.rb
|
253
|
+
- spec/rails_app/spec/models/refresh_token_spec.rb
|
252
254
|
- spec/rails_app/spec/models/user_spec.rb
|
253
255
|
- spec/rails_app/spec/spec_helper.rb
|
254
256
|
- spec/rails_app/vendor/plugins/.gitkeep
|