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