doorkeeper 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 923df84b3da199e68a3cce57633ad6b0e1257aab
4
- data.tar.gz: 16f3055a34246abe9db2b2110e1c9ca0c00a5f60
3
+ metadata.gz: 598bf74d593976f9ffe96a0e511f9b75b0e6f1e4
4
+ data.tar.gz: cddda3ef65df20171860bba9cbecb8097873f039
5
5
  SHA512:
6
- metadata.gz: 2e69a18ef913b226db002e6bf5b7f850dd49a6fe8db9c78b74862e6b726b2ccdc79e03e03b303e38306244589540716540464b9d1b5240f4f9a9b14484202473
7
- data.tar.gz: 4dbd9358b6d3ecf463947ff299000e5ed43b71f8bb7d624c370f34b32a627abea2ac17c412df679b3a3218dd570ee05981a8cdb1dce6488feb9a9f40e0758a5c
6
+ metadata.gz: 8b66fe347a4767a45fe3ede635b53de57ca8a20e201a03872d5e80295d5a3701f33fdc78ba5437e3e8e13b2c7ba3eb7f04f87ff52708a9d04ea29ca2ae819984
7
+ data.tar.gz: 74a12fac73bb6e735bb16a3eea2d050299e6204a4f095fab95c605d178ebaec0f47871e4d815b30a873b5a7bc4e1dadeaaf7c9c23cc1ea3d15fc598569114530
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0
4
+
5
+ - enhancements
6
+ - [#387] Adds reuse_access_token configuration option.
7
+
3
8
  ## 1.2.0
4
9
 
5
10
  - enhancements
data/lib/doorkeeper.rb CHANGED
@@ -21,6 +21,7 @@ require 'doorkeeper/oauth/code_response'
21
21
  require 'doorkeeper/oauth/token_response'
22
22
  require 'doorkeeper/oauth/error_response'
23
23
  require 'doorkeeper/oauth/pre_authorization'
24
+ require 'doorkeeper/oauth/request_concern'
24
25
  require 'doorkeeper/oauth/authorization_code_request'
25
26
  require 'doorkeeper/oauth/refresh_token_request'
26
27
  require 'doorkeeper/oauth/password_access_token_request'
@@ -81,6 +81,10 @@ module Doorkeeper
81
81
  def realm(realm)
82
82
  @config.instance_variable_set('@realm', realm)
83
83
  end
84
+
85
+ def reuse_access_token
86
+ @config.instance_variable_set("@reuse_access_token", true)
87
+ end
84
88
  end
85
89
 
86
90
  module Option
@@ -171,6 +175,8 @@ module Doorkeeper
171
175
  option :grant_flows,
172
176
  default: %w(authorization_code implicit password client_credentials)
173
177
 
178
+ attr_reader :reuse_access_token
179
+
174
180
  def refresh_token_enabled?
175
181
  !!@refresh_token_enabled
176
182
  end
@@ -6,7 +6,9 @@ module Doorkeeper
6
6
  include Doorkeeper::Models::Accessible
7
7
  include Doorkeeper::Models::Scopes
8
8
 
9
- belongs_to :application, class_name: 'Doorkeeper::Application', inverse_of: :access_tokens
9
+ belongs_to :application,
10
+ class_name: 'Doorkeeper::Application',
11
+ inverse_of: :access_tokens
10
12
 
11
13
  validates :token, presence: true
12
14
  validates :token, uniqueness: true
@@ -14,11 +16,14 @@ module Doorkeeper
14
16
 
15
17
  attr_accessor :use_refresh_token
16
18
  if ::Rails.version.to_i < 4 || defined?(ProtectedAttributes)
17
- attr_accessible :application_id, :resource_owner_id, :expires_in, :scopes, :use_refresh_token
19
+ attr_accessible :application_id, :resource_owner_id, :expires_in,
20
+ :scopes, :use_refresh_token
18
21
  end
19
22
 
20
23
  before_validation :generate_token, on: :create
21
- before_validation :generate_refresh_token, on: :create, if: :use_refresh_token?
24
+ before_validation :generate_refresh_token,
25
+ on: :create,
26
+ if: :use_refresh_token?
22
27
 
23
28
  def self.authenticate(token)
24
29
  where(token: token).first
@@ -36,31 +41,52 @@ module Doorkeeper
36
41
  end
37
42
 
38
43
  def self.matching_token_for(application, resource_owner_or_id, scopes)
39
- resource_owner_id = resource_owner_or_id.respond_to?(:to_key) ? resource_owner_or_id.id : resource_owner_or_id
40
- token = last_authorized_token_for(application, resource_owner_id)
44
+ resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
45
+ resource_owner_or_id.id
46
+ else
47
+ resource_owner_or_id
48
+ end
49
+ token = last_authorized_token_for(application.id, resource_owner_id)
41
50
  token if token && ScopeChecker.matches?(token.scopes, scopes)
42
51
  end
43
52
 
53
+ def self.find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
54
+ if Doorkeeper.configuration.reuse_access_token
55
+ access_token = matching_token_for(application, resource_owner_id, scopes)
56
+ if access_token && !access_token.expired?
57
+ return access_token
58
+ end
59
+ end
60
+ create!(
61
+ application_id: application.try(:id),
62
+ resource_owner_id: resource_owner_id,
63
+ scopes: scopes.to_s,
64
+ expires_in: expires_in,
65
+ use_refresh_token: use_refresh_token
66
+ )
67
+ end
68
+
44
69
  def token_type
45
70
  'bearer'
46
71
  end
47
72
 
48
73
  def use_refresh_token?
49
- self.use_refresh_token
74
+ use_refresh_token
50
75
  end
51
76
 
52
77
  def as_json(options = {})
53
78
  {
54
- resource_owner_id: self.resource_owner_id,
55
- scopes: self.scopes,
56
- expires_in_seconds: self.expires_in_seconds,
57
- application: { uid: self.application.uid }
79
+ resource_owner_id: resource_owner_id,
80
+ scopes: scopes,
81
+ expires_in_seconds: expires_in_seconds,
82
+ application: { uid: application.uid }
58
83
  }
59
84
  end
60
85
 
61
86
  # It indicates whether the tokens have the same credential
62
87
  def same_credential?(access_token)
63
- application_id == access_token.application_id && resource_owner_id == access_token.resource_owner_id
88
+ application_id == access_token.application_id &&
89
+ resource_owner_id == access_token.resource_owner_id
64
90
  end
65
91
 
66
92
  private
@@ -12,8 +12,8 @@ module Doorkeeper
12
12
  end
13
13
  private_class_method :delete_all_for
14
14
 
15
- def self.last_authorized_token_for(application, resource_owner_id)
16
- where(application_id: application.id,
15
+ def self.last_authorized_token_for(application_id, resource_owner_id)
16
+ where(application_id: application_id,
17
17
  resource_owner_id: resource_owner_id,
18
18
  revoked_at: nil).
19
19
  order('created_at desc').
@@ -29,8 +29,8 @@ module Doorkeeper
29
29
  end
30
30
  private_class_method :delete_all_for
31
31
 
32
- def self.last_authorized_token_for(application, resource_owner_id)
33
- where(application_id: application.id,
32
+ def self.last_authorized_token_for(application_id, resource_owner_id)
33
+ where(application_id: application_id,
34
34
  resource_owner_id: resource_owner_id,
35
35
  revoked_at: nil).
36
36
  sort(:created_at.desc).
@@ -24,8 +24,8 @@ module Doorkeeper
24
24
  end
25
25
  private_class_method :delete_all_for
26
26
 
27
- def self.last_authorized_token_for(application, resource_owner_id)
28
- where(application_id: application.id,
27
+ def self.last_authorized_token_for(application_id, resource_owner_id)
28
+ where(application_id: application_id,
29
29
  resource_owner_id: resource_owner_id,
30
30
  revoked_at: nil).
31
31
  order_by([:created_at, :desc]).
@@ -31,8 +31,8 @@ module Doorkeeper
31
31
  end
32
32
  private_class_method :delete_all_for
33
33
 
34
- def self.last_authorized_token_for(application, resource_owner_id)
35
- where(application_id: application.id,
34
+ def self.last_authorized_token_for(application_id, resource_owner_id)
35
+ where(application_id: application_id,
36
36
  resource_owner_id: resource_owner_id,
37
37
  revoked_at: nil).
38
38
  order_by([:created_at, :desc]).
@@ -2,6 +2,7 @@ module Doorkeeper
2
2
  module OAuth
3
3
  class AuthorizationCodeRequest
4
4
  include Doorkeeper::Validations
5
+ include Doorkeeper::OAuth::RequestConcern
5
6
 
6
7
  validate :attributes, error: :invalid_request
7
8
  validate :client, error: :invalid_client
@@ -17,30 +18,14 @@ module Doorkeeper
17
18
  @redirect_uri = parameters[:redirect_uri]
18
19
  end
19
20
 
20
- def authorize
21
- validate
22
- @response = if valid?
23
- grant.revoke
24
- issue_token
25
- TokenResponse.new access_token
26
- else
27
- ErrorResponse.from_request self
28
- end
29
- end
30
-
31
- def valid?
32
- error.nil?
33
- end
34
-
35
21
  private
36
22
 
37
- def issue_token
38
- @access_token = Doorkeeper::AccessToken.create!(
39
- application_id: grant.application_id,
40
- resource_owner_id: grant.resource_owner_id,
41
- scopes: grant.scopes_string,
42
- expires_in: server.access_token_expires_in,
43
- use_refresh_token: server.refresh_token_enabled?)
23
+ def on_successful_authorization
24
+ grant.revoke
25
+ find_or_create_access_token(grant.application,
26
+ grant.resource_owner_id,
27
+ grant.scopes,
28
+ server)
44
29
  end
45
30
 
46
31
  def validate_attributes
@@ -5,6 +5,9 @@ require 'doorkeeper/oauth/client_credentials/validation'
5
5
  module Doorkeeper
6
6
  module OAuth
7
7
  class ClientCredentialsRequest
8
+ include Doorkeeper::Validations
9
+ include Doorkeeper::OAuth::RequestConcern
10
+
8
11
  attr_accessor :issuer, :server, :client, :original_scopes, :scopes
9
12
  attr_reader :response
10
13
  alias :error_response :response
@@ -21,16 +24,11 @@ module Doorkeeper
21
24
  @original_scopes = parameters[:scope]
22
25
  end
23
26
 
24
- def authorize
25
- status = issuer.create(client, scopes)
26
- @response = if status
27
- TokenResponse.new(issuer.token)
28
- else
29
- ErrorResponse.from_request(self)
30
- end
27
+ def access_token
28
+ issuer.token
31
29
  end
32
30
 
33
- # TODO: duplicated code in all flows
31
+ # TODO: Why can't it use RequestConcern's implementation?
34
32
  def scopes
35
33
  @scopes ||= if @original_scopes.present?
36
34
  Doorkeeper::OAuth::Scopes.from_string(@original_scopes)
@@ -38,6 +36,12 @@ module Doorkeeper
38
36
  server.default_scopes
39
37
  end
40
38
  end
39
+
40
+ private
41
+
42
+ def valid?
43
+ issuer.create(client, scopes)
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -29,7 +29,9 @@ module Doorkeeper
29
29
  state: pre_auth.state
30
30
  )
31
31
  else
32
- uri_with_query pre_auth.redirect_uri, code: auth.token.token, state: pre_auth.state
32
+ uri_with_query pre_auth.redirect_uri,
33
+ code: auth.token.token,
34
+ state: pre_auth.state
33
35
  end
34
36
  end
35
37
  end
@@ -18,7 +18,11 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  def body
21
- { error: name, error_description: description, state: state }.reject { |k, v| v.blank? }
21
+ {
22
+ error: name,
23
+ error_description: description,
24
+ state: state
25
+ }.reject { |_, v| v.blank? }
22
26
  end
23
27
 
24
28
  def status
@@ -26,7 +30,8 @@ module Doorkeeper
26
30
  end
27
31
 
28
32
  def redirectable?
29
- (name != :invalid_redirect_uri) && (name != :invalid_client) && !URIChecker.test_uri?(@redirect_uri)
33
+ name != :invalid_redirect_uri && name != :invalid_client &&
34
+ !URIChecker.test_uri?(@redirect_uri)
30
35
  end
31
36
 
32
37
  def redirect_uri
@@ -20,7 +20,8 @@ module Doorkeeper
20
20
  end
21
21
 
22
22
  def description
23
- @description ||= I18n.translate @reason, scope: [:doorkeeper, :errors, :messages, :invalid_token]
23
+ scope = { scope: [:doorkeeper, :errors, :messages, :invalid_token] }
24
+ @description ||= I18n.translate @reason, scope
24
25
  end
25
26
  end
26
27
  end
@@ -2,6 +2,7 @@ module Doorkeeper
2
2
  module OAuth
3
3
  class PasswordAccessTokenRequest
4
4
  include Doorkeeper::Validations
5
+ include Doorkeeper::OAuth::RequestConcern
5
6
  include Doorkeeper::OAuth::Helpers
6
7
 
7
8
  validate :client, error: :invalid_client
@@ -23,38 +24,10 @@ module Doorkeeper
23
24
  end
24
25
  end
25
26
 
26
- def authorize
27
- validate
28
- @response = if valid?
29
- issue_token
30
- TokenResponse.new access_token
31
- else
32
- ErrorResponse.from_request self
33
- end
34
- end
35
-
36
- def valid?
37
- error.nil?
38
- end
39
-
40
- def scopes
41
- @scopes ||= if @original_scopes.present?
42
- Doorkeeper::OAuth::Scopes.from_string(@original_scopes)
43
- else
44
- server.default_scopes
45
- end
46
- end
47
-
48
27
  private
49
28
 
50
- def issue_token
51
- @access_token = Doorkeeper::AccessToken.create!(
52
- application_id: client.try(:id),
53
- resource_owner_id: resource_owner.id,
54
- scopes: scopes.to_s,
55
- expires_in: server.access_token_expires_in,
56
- use_refresh_token: server.refresh_token_enabled?
57
- )
29
+ def on_successful_authorization
30
+ find_or_create_access_token(client, resource_owner.id, scopes, server)
58
31
  end
59
32
 
60
33
  def validate_scopes
@@ -2,6 +2,7 @@ module Doorkeeper
2
2
  module OAuth
3
3
  class RefreshTokenRequest
4
4
  include Doorkeeper::Validations
5
+ include Doorkeeper::OAuth::RequestConcern
5
6
  include Doorkeeper::OAuth::Helpers
6
7
 
7
8
  validate :token, error: :invalid_request
@@ -16,7 +17,7 @@ module Doorkeeper
16
17
  @server = server
17
18
  @refresh_token = refresh_token
18
19
  @credentials = credentials
19
- @requested_scopes = parameters[:scopes]
20
+ @original_scopes = parameters[:scopes]
20
21
 
21
22
  if credentials
22
23
  @client = Doorkeeper::Application.authenticate credentials.uid,
@@ -24,43 +25,24 @@ module Doorkeeper
24
25
  end
25
26
  end
26
27
 
27
- def authorize
28
- validate
29
- @response = if valid?
30
- revoke_and_create_access_token
31
- TokenResponse.new access_token
32
- else
33
- ErrorResponse.from_request self
34
- end
35
- end
36
-
37
- def valid?
38
- error.nil?
39
- end
40
-
41
- def scopes
42
- @scopes ||= if @requested_scopes.present?
43
- Scopes.from_string @requested_scopes
44
- else
45
- refresh_token.scopes
46
- end
47
- end
48
-
49
28
  private
50
29
 
51
- def revoke_and_create_access_token
30
+ def on_successful_authorization
52
31
  refresh_token.revoke
53
32
  create_access_token
54
33
  end
55
34
 
35
+ def default_scopes
36
+ refresh_token.scopes
37
+ end
38
+
56
39
  def create_access_token
57
40
  @access_token = Doorkeeper::AccessToken.create!(
58
41
  application_id: refresh_token.application_id,
59
42
  resource_owner_id: refresh_token.resource_owner_id,
60
43
  scopes: scopes.to_s,
61
44
  expires_in: server.access_token_expires_in,
62
- use_refresh_token: true
63
- )
45
+ use_refresh_token: true)
64
46
  end
65
47
 
66
48
  def validate_token
@@ -76,8 +58,8 @@ module Doorkeeper
76
58
  end
77
59
 
78
60
  def validate_scope
79
- if @requested_scopes.present?
80
- ScopeChecker.valid?(@requested_scopes, refresh_token.scopes)
61
+ if @original_scopes.present?
62
+ ScopeChecker.valid?(@original_scopes, refresh_token.scopes)
81
63
  else
82
64
  true
83
65
  end
@@ -0,0 +1,43 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module RequestConcern
4
+ def authorize
5
+ validate
6
+ if valid?
7
+ on_successful_authorization
8
+ @response = TokenResponse.new(access_token)
9
+ else
10
+ @response = ErrorResponse.from_request(self)
11
+ end
12
+ end
13
+
14
+ def scopes
15
+ @scopes ||= if @original_scopes.present?
16
+ Doorkeeper::OAuth::Scopes.from_string(@original_scopes)
17
+ else
18
+ default_scopes
19
+ end
20
+ end
21
+
22
+ def default_scopes
23
+ server.default_scopes
24
+ end
25
+
26
+ def valid?
27
+ error.nil?
28
+ end
29
+
30
+ def find_or_create_access_token(client, resource_owner_id, scopes, server)
31
+ @access_token = Doorkeeper::AccessToken.find_or_create_for(
32
+ client,
33
+ resource_owner_id,
34
+ scopes,
35
+ server.access_token_expires_in,
36
+ server.refresh_token_enabled?)
37
+ end
38
+
39
+ def on_successful_authorization
40
+ end
41
+ end
42
+ end
43
+ end
@@ -22,7 +22,9 @@ module Doorkeeper
22
22
  end
23
23
 
24
24
  def headers
25
- { 'Cache-Control' => 'no-store', 'Pragma' => 'no-cache', 'Content-Type' => 'application/json; charset=utf-8' }
25
+ { 'Cache-Control' => 'no-store',
26
+ 'Pragma' => 'no-cache',
27
+ 'Content-Type' => 'application/json; charset=utf-8' }
26
28
  end
27
29
  end
28
30
  end
@@ -1,3 +1,3 @@
1
1
  module Doorkeeper
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -25,6 +25,10 @@ Doorkeeper.configure do
25
25
  # If you want to disable expiration, set this to nil.
26
26
  # access_token_expires_in 2.hours
27
27
 
28
+ # Reuse access token for the same resource owner within an application (disabled by default)
29
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
30
+ # reuse_access_token
31
+
28
32
  # Issue access tokens with refresh token (disabled by default)
29
33
  # use_refresh_token
30
34
 
@@ -62,5 +62,14 @@ module Doorkeeper::OAuth
62
62
  subject.validate
63
63
  expect(subject.error).to eq(:invalid_grant)
64
64
  end
65
+
66
+ it 'skips token creation if there is a matching one' do
67
+ Doorkeeper.configuration.stub(:reuse_access_token).and_return(true)
68
+ FactoryGirl.create(:access_token, application_id: client.id,
69
+ resource_owner_id: grant.resource_owner_id, scopes: grant.scopes.to_s)
70
+ expect do
71
+ subject.authorize
72
+ end.to_not change { Doorkeeper::AccessToken.count }
73
+ end
65
74
  end
66
75
  end
@@ -44,6 +44,21 @@ module Doorkeeper::OAuth
44
44
  expect(subject).to be_valid
45
45
  end
46
46
 
47
+ it 'creates token even when there is already one (default)' do
48
+ FactoryGirl.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
49
+ expect do
50
+ subject.authorize
51
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
52
+ end
53
+
54
+ it 'skips token creation if there is already one' do
55
+ Doorkeeper.configuration.stub(:reuse_access_token).and_return(true)
56
+ FactoryGirl.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
57
+ expect do
58
+ subject.authorize
59
+ end.to_not change { Doorkeeper::AccessToken.count }
60
+ end
61
+
47
62
  describe 'with scopes' do
48
63
  subject do
49
64
  PasswordAccessTokenRequest.new(server, client, owner, scope: 'public')
@@ -7,7 +7,7 @@ describe Doorkeeper::AccessGrant do
7
7
 
8
8
  it_behaves_like 'an accessible token'
9
9
  it_behaves_like 'a revocable token'
10
- it_behaves_like 'an unique token' do
10
+ it_behaves_like 'a unique token' do
11
11
  let(:factory_name) { :access_grant }
12
12
  end
13
13
 
@@ -8,7 +8,7 @@ module Doorkeeper
8
8
 
9
9
  it_behaves_like 'an accessible token'
10
10
  it_behaves_like 'a revocable token'
11
- it_behaves_like 'an unique token' do
11
+ it_behaves_like 'a unique token' do
12
12
  let(:factory_name) { :access_token }
13
13
  end
14
14
 
@@ -58,6 +58,18 @@ feature 'Resource Owner Password Credentials Flow' do
58
58
 
59
59
  should_have_json 'refresh_token', token.refresh_token
60
60
  end
61
+
62
+ scenario 'should return the same token if it is still accessible' do
63
+ Doorkeeper.configuration.stub(:reuse_access_token).and_return(true)
64
+
65
+ client_is_authorized(@client, @resource_owner)
66
+
67
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
68
+
69
+ Doorkeeper::AccessToken.count.should be(1)
70
+
71
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
72
+ end
61
73
  end
62
74
 
63
75
  context 'with invalid user credentials' do
@@ -27,16 +27,8 @@ shared_examples 'a revocable token' do
27
27
  end
28
28
  end
29
29
 
30
- shared_examples 'an unique token' do
30
+ shared_examples 'a unique token' do
31
31
  describe :token do
32
- it 'is unique' do
33
- tokens = []
34
- 3.times do
35
- token = FactoryGirl.create(factory_name).token
36
- expect(tokens).not_to include(token)
37
- end
38
- end
39
-
40
32
  it 'is generated before validation' do
41
33
  expect { subject.valid? }.to change { subject.token }.from(nil)
42
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-03 00:00:00.000000000 Z
12
+ date: 2014-05-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -231,6 +231,7 @@ files:
231
231
  - lib/doorkeeper/oauth/password_access_token_request.rb
232
232
  - lib/doorkeeper/oauth/pre_authorization.rb
233
233
  - lib/doorkeeper/oauth/refresh_token_request.rb
234
+ - lib/doorkeeper/oauth/request_concern.rb
234
235
  - lib/doorkeeper/oauth/scopes.rb
235
236
  - lib/doorkeeper/oauth/token.rb
236
237
  - lib/doorkeeper/oauth/token_request.rb