doorkeeper 4.1.0 → 4.2.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 +4 -4
- data/Gemfile +1 -1
- data/NEWS.md +8 -0
- data/README.md +1 -0
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -0
- data/app/controllers/doorkeeper/tokens_controller.rb +49 -13
- data/lib/doorkeeper/version.rb +1 -1
- data/spec/controllers/application_metal_controller.rb +10 -0
- data/spec/controllers/tokens_controller_spec.rb +1 -1
- data/spec/factories.rb +3 -1
- data/spec/models/doorkeeper/application_spec.rb +1 -1
- data/spec/requests/flows/revoke_token_spec.rb +100 -86
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9886d44f18692bd1207493bc04efbad8ccab4e46
|
4
|
+
data.tar.gz: 56c74dd71ce5473c11e1ce3a5c6aeb7a264c0ee8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25db8a95095afb722206d05f2e42cf98964126329686363c5d17afd6f0a9bbe4ba0cfc224ba60544b6c3c7939351a6396aac7789394b2bb2fddc2c60b9814fa5
|
7
|
+
data.tar.gz: 007df96482ad0b1240ae3bd9089eaefa8a9b5d7fc90c40817dfde978baed97051042233980f2762735eae2b2cda1bba8c033774ecb00cbd01a8bcb27f6fb2fbf
|
data/Gemfile
CHANGED
data/NEWS.md
CHANGED
@@ -4,6 +4,14 @@ User-visible changes worth mentioning.
|
|
4
4
|
|
5
5
|
## master
|
6
6
|
|
7
|
+
## 4.2.0
|
8
|
+
|
9
|
+
- Security fix: Address CVE-2016-6582, implement token revocation according to
|
10
|
+
spec (tokens might not be revoked if client follows the spec).
|
11
|
+
- [#873] Add hooks to Doorkeeper::ApplicationMetalController
|
12
|
+
- [#871] Allow downstream users to better utilize doorkeeper spec factories by
|
13
|
+
eliminating name conflict on `:user` factory.
|
14
|
+
|
7
15
|
## 4.1.0
|
8
16
|
|
9
17
|
- [#845] Allow customising the `Doorkeeper::ApplicationController` base
|
data/README.md
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
[![Dependency Status](https://gemnasium.com/doorkeeper-gem/doorkeeper.svg?travis)](https://gemnasium.com/doorkeeper-gem/doorkeeper)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/doorkeeper-gem/doorkeeper.svg)](https://codeclimate.com/github/doorkeeper-gem/doorkeeper)
|
6
6
|
[![Gem Version](https://badge.fury.io/rb/doorkeeper.svg)](https://rubygems.org/gems/doorkeeper)
|
7
|
+
[![Security](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master.svg)](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master)
|
7
8
|
|
8
9
|
Doorkeeper is a gem that makes it easy to introduce OAuth 2 provider
|
9
10
|
functionality to your Rails or Grape application.
|
@@ -11,29 +11,65 @@ module Doorkeeper
|
|
11
11
|
|
12
12
|
# OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
|
13
13
|
def revoke
|
14
|
-
# The authorization server first
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
# The authorization server, if applicable, first authenticates the client
|
15
|
+
# and checks its ownership of the provided token.
|
16
|
+
#
|
17
|
+
# Doorkeeper does not use the token_type_hint logic described in the
|
18
|
+
# RFC 7009 due to the refresh token implementation that is a field in
|
19
|
+
# the access token model.
|
20
|
+
if authorized?
|
21
|
+
revoke_token
|
19
22
|
end
|
20
|
-
|
21
|
-
#
|
23
|
+
|
24
|
+
# The authorization server responds with HTTP status code 200 if the token
|
25
|
+
# has been revoked successfully or if the client submitted an invalid
|
26
|
+
# token
|
22
27
|
render json: {}, status: 200
|
23
28
|
end
|
24
29
|
|
25
30
|
private
|
26
31
|
|
27
|
-
|
28
|
-
|
29
|
-
|
32
|
+
# OAuth 2.0 Section 2.1 defines two client types, "public" & "confidential".
|
33
|
+
# Public clients (as per RFC 7009) do not require authentication whereas
|
34
|
+
# confidential clients must be authenticated for their token revocation.
|
35
|
+
#
|
36
|
+
# Once a confidential client is authenticated, it must be authorized to
|
37
|
+
# revoke the provided access or refresh token. This ensures one client
|
38
|
+
# cannot revoke another's tokens.
|
39
|
+
#
|
40
|
+
# Doorkeeper determines the client type implicitly via the presence of the
|
41
|
+
# OAuth client associated with a given access or refresh token. Since public
|
42
|
+
# clients authenticate the resource owner via "password" or "implicit" grant
|
43
|
+
# types, they set the application_id as null (since the claim cannot be
|
44
|
+
# verified).
|
45
|
+
#
|
46
|
+
# https://tools.ietf.org/html/rfc6749#section-2.1
|
47
|
+
# https://tools.ietf.org/html/rfc7009
|
48
|
+
def authorized?
|
49
|
+
if token.present?
|
50
|
+
# Client is confidential, therefore client authentication & authorization
|
51
|
+
# is required
|
52
|
+
if token.application_id?
|
53
|
+
# We authorize client by checking token's application
|
54
|
+
server.client && server.client.application == token.application
|
55
|
+
else
|
56
|
+
# Client is public, authentication unnecessary
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def revoke_token
|
63
|
+
if token.accessible?
|
30
64
|
token.revoke
|
31
|
-
true
|
32
|
-
else
|
33
|
-
false
|
34
65
|
end
|
35
66
|
end
|
36
67
|
|
68
|
+
def token
|
69
|
+
@token ||= AccessToken.by_token(request.POST['token']) ||
|
70
|
+
AccessToken.by_refresh_token(request.POST['token'])
|
71
|
+
end
|
72
|
+
|
37
73
|
def strategy
|
38
74
|
@strategy ||= server.token_request params[:grant_type]
|
39
75
|
end
|
data/lib/doorkeeper/version.rb
CHANGED
@@ -64,7 +64,7 @@ describe Doorkeeper::TokensController do
|
|
64
64
|
describe 'when revoke authorization has failed' do
|
65
65
|
# http://tools.ietf.org/html/rfc7009#section-2.2
|
66
66
|
it 'returns no error response' do
|
67
|
-
token = double(:token, authorize: false)
|
67
|
+
token = double(:token, authorize: false, application_id?: true)
|
68
68
|
allow(controller).to receive(:token) { token }
|
69
69
|
|
70
70
|
post :revoke
|
data/spec/factories.rb
CHANGED
@@ -22,5 +22,7 @@ FactoryGirl.define do
|
|
22
22
|
redirect_uri 'https://app.com/callback'
|
23
23
|
end
|
24
24
|
|
25
|
-
factory :user
|
25
|
+
# do not name this factory :user, otherwise it will conflict with factories
|
26
|
+
# from applications that use doorkeeper factories in their own tests
|
27
|
+
factory :doorkeeper_testing_user, class: :user
|
26
28
|
end
|
@@ -8,135 +8,149 @@ describe 'Revoke Token Flow' do
|
|
8
8
|
context 'with default parameters' do
|
9
9
|
let(:client_application) { FactoryGirl.create :application }
|
10
10
|
let(:resource_owner) { User.create!(name: 'John', password: 'sekret') }
|
11
|
-
let(:
|
11
|
+
let(:access_token) do
|
12
12
|
FactoryGirl.create(:access_token,
|
13
13
|
application: client_application,
|
14
14
|
resource_owner_id: resource_owner.id,
|
15
15
|
use_refresh_token: true)
|
16
16
|
end
|
17
|
-
let(:headers) { { 'HTTP_AUTHORIZATION' => "Bearer #{authorization_access_token.token}" } }
|
18
17
|
|
19
|
-
context '
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# has been revoked successfully or if the client submitted an invalid token.
|
26
|
-
expect(response).to be_success
|
27
|
-
expect(authorization_access_token).to_not be_revoked
|
18
|
+
context 'with authenticated, confidential OAuth 2.0 client/application' do
|
19
|
+
let(:headers) do
|
20
|
+
client_id = client_application.uid
|
21
|
+
client_secret = client_application.secret
|
22
|
+
credentials = Base64.encode64("#{client_id}:#{client_secret}")
|
23
|
+
{ 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
|
28
24
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'The access token to revoke is the same than the authorization access token' do
|
32
|
-
let(:token_to_revoke) { authorization_access_token }
|
33
25
|
|
34
|
-
it '
|
35
|
-
post revocation_token_endpoint_url, { token:
|
26
|
+
it 'should revoke the access token provided' do
|
27
|
+
post revocation_token_endpoint_url, { token: access_token.token }, headers
|
36
28
|
|
37
|
-
|
38
|
-
authorization_access_token.reload
|
29
|
+
access_token.reload
|
39
30
|
|
40
31
|
expect(response).to be_success
|
41
|
-
expect(
|
42
|
-
expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_truthy
|
32
|
+
expect(access_token.revoked?).to be_truthy
|
43
33
|
end
|
44
34
|
|
45
|
-
it '
|
46
|
-
|
47
|
-
post url_with_query_string, {}, headers
|
35
|
+
it 'should revoke the refresh token provided' do
|
36
|
+
post revocation_token_endpoint_url, { token: access_token.refresh_token }, headers
|
48
37
|
|
49
|
-
|
50
|
-
authorization_access_token.reload
|
38
|
+
access_token.reload
|
51
39
|
|
52
40
|
expect(response).to be_success
|
53
|
-
expect(
|
54
|
-
expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_falsey
|
55
|
-
expect(authorization_access_token.revoked?).to be_falsey
|
41
|
+
expect(access_token.revoked?).to be_truthy
|
56
42
|
end
|
57
|
-
end
|
58
43
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
44
|
+
context 'with invalid token to revoke' do
|
45
|
+
it 'should not revoke any tokens and respond successfully' do
|
46
|
+
num_prev_revoked_tokens = Doorkeeper::AccessToken.where(revoked_at: nil).count
|
47
|
+
post revocation_token_endpoint_url, { token: 'I_AM_AN_INVALID_TOKEN' }, headers
|
48
|
+
|
49
|
+
# The authorization server responds with HTTP status code 200 even if
|
50
|
+
# token is invalid
|
51
|
+
expect(response).to be_success
|
52
|
+
expect(Doorkeeper::AccessToken.where(revoked_at: nil).count).to eq(num_prev_revoked_tokens)
|
53
|
+
end
|
65
54
|
end
|
66
55
|
|
67
|
-
|
68
|
-
|
56
|
+
context 'with bad credentials and a valid token' do
|
57
|
+
let(:headers) do
|
58
|
+
client_id = client_application.uid
|
59
|
+
credentials = Base64.encode64("#{client_id}:poop")
|
60
|
+
{ 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
|
61
|
+
end
|
62
|
+
it 'should not revoke any tokens and respond successfully' do
|
63
|
+
post revocation_token_endpoint_url, { token: access_token.token }, headers
|
69
64
|
|
70
|
-
|
71
|
-
authorization_access_token.reload
|
65
|
+
access_token.reload
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
expect(authorization_access_token.revoked?).to be_falsey
|
67
|
+
expect(response).to be_success
|
68
|
+
expect(access_token.revoked?).to be_falsey
|
69
|
+
end
|
77
70
|
end
|
78
|
-
end
|
79
71
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
72
|
+
context 'with no credentials and a valid token' do
|
73
|
+
it 'should not revoke any tokens and respond successfully' do
|
74
|
+
post revocation_token_endpoint_url, { token: access_token.token }
|
75
|
+
|
76
|
+
access_token.reload
|
77
|
+
|
78
|
+
expect(response).to be_success
|
79
|
+
expect(access_token.revoked?).to be_falsey
|
80
|
+
end
|
87
81
|
end
|
88
82
|
|
89
|
-
|
90
|
-
|
83
|
+
context 'with valid token for another client application' do
|
84
|
+
let(:other_client_application) { FactoryGirl.create :application }
|
85
|
+
let(:headers) do
|
86
|
+
client_id = other_client_application.uid
|
87
|
+
client_secret = other_client_application.secret
|
88
|
+
credentials = Base64.encode64("#{client_id}:#{client_secret}")
|
89
|
+
{ 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
|
90
|
+
end
|
91
91
|
|
92
|
-
|
93
|
-
|
92
|
+
it 'should not revoke the token as its unauthorized' do
|
93
|
+
post revocation_token_endpoint_url, { token: access_token.token }, headers
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
access_token.reload
|
96
|
+
|
97
|
+
expect(response).to be_success
|
98
|
+
expect(access_token.revoked?).to be_falsey
|
99
|
+
end
|
99
100
|
end
|
100
101
|
end
|
101
102
|
|
102
|
-
context '
|
103
|
-
let(:
|
104
|
-
let(:token_to_revoke) do
|
103
|
+
context 'with public OAuth 2.0 client/application' do
|
104
|
+
let(:access_token) do
|
105
105
|
FactoryGirl.create(:access_token,
|
106
|
-
application:
|
107
|
-
resource_owner_id:
|
106
|
+
application: nil,
|
107
|
+
resource_owner_id: resource_owner.id,
|
108
108
|
use_refresh_token: true)
|
109
109
|
end
|
110
110
|
|
111
|
-
it '
|
112
|
-
post revocation_token_endpoint_url, { token:
|
111
|
+
it 'should revoke the access token provided' do
|
112
|
+
post revocation_token_endpoint_url, { token: access_token.token }
|
113
113
|
|
114
|
-
|
115
|
-
authorization_access_token.reload
|
114
|
+
access_token.reload
|
116
115
|
|
117
116
|
expect(response).to be_success
|
118
|
-
expect(
|
119
|
-
expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_falsey
|
120
|
-
expect(authorization_access_token.revoked?).to be_falsey
|
117
|
+
expect(access_token.revoked?).to be_truthy
|
121
118
|
end
|
122
|
-
end
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
FactoryGirl.create(:access_token,
|
127
|
-
application: client_application,
|
128
|
-
resource_owner_id: resource_owner.id,
|
129
|
-
use_refresh_token: true)
|
130
|
-
end
|
120
|
+
it 'should revoke the refresh token provided' do
|
121
|
+
post revocation_token_endpoint_url, { token: access_token.refresh_token }
|
131
122
|
|
132
|
-
|
133
|
-
post revocation_token_endpoint_url, { token: token_to_revoke.refresh_token, token_type_hint: 'refresh_token' }, headers
|
134
|
-
authorization_access_token.reload
|
135
|
-
token_to_revoke.reload
|
123
|
+
access_token.reload
|
136
124
|
|
137
125
|
expect(response).to be_success
|
138
|
-
expect(
|
139
|
-
|
126
|
+
expect(access_token.revoked?).to be_truthy
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'with a valid token issued for a confidential client' do
|
130
|
+
let(:access_token) do
|
131
|
+
FactoryGirl.create(:access_token,
|
132
|
+
application: client_application,
|
133
|
+
resource_owner_id: resource_owner.id,
|
134
|
+
use_refresh_token: true)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should not revoke the access token provided' do
|
138
|
+
post revocation_token_endpoint_url, { token: access_token.token }
|
139
|
+
|
140
|
+
access_token.reload
|
141
|
+
|
142
|
+
expect(response).to be_success
|
143
|
+
expect(access_token.revoked?).to be_falsey
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should not revoke the refresh token provided' do
|
147
|
+
post revocation_token_endpoint_url, { token: access_token.token }
|
148
|
+
|
149
|
+
access_token.reload
|
150
|
+
|
151
|
+
expect(response).to be_success
|
152
|
+
expect(access_token.revoked?).to be_falsey
|
153
|
+
end
|
140
154
|
end
|
141
155
|
end
|
142
156
|
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: 4.
|
4
|
+
version: 4.2.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: 2016-
|
12
|
+
date: 2016-08-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: railties
|
@@ -239,6 +239,7 @@ files:
|
|
239
239
|
- lib/generators/doorkeeper/templates/initializer.rb
|
240
240
|
- lib/generators/doorkeeper/templates/migration.rb
|
241
241
|
- lib/generators/doorkeeper/views_generator.rb
|
242
|
+
- spec/controllers/application_metal_controller.rb
|
242
243
|
- spec/controllers/applications_controller_spec.rb
|
243
244
|
- spec/controllers/authorizations_controller_spec.rb
|
244
245
|
- spec/controllers/protected_resources_controller_spec.rb
|
@@ -383,6 +384,7 @@ signing_key:
|
|
383
384
|
specification_version: 4
|
384
385
|
summary: OAuth 2 provider for Rails and Grape
|
385
386
|
test_files:
|
387
|
+
- spec/controllers/application_metal_controller.rb
|
386
388
|
- spec/controllers/applications_controller_spec.rb
|
387
389
|
- spec/controllers/authorizations_controller_spec.rb
|
388
390
|
- spec/controllers/protected_resources_controller_spec.rb
|