doorkeeper-device_authorization_grant 0.1.1 → 1.0.1
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +9 -25
- data/Rakefile +1 -15
- data/app/controllers/doorkeeper/device_authorization_grant/device_codes_controller.rb +1 -1
- data/config/locales/en.yml +7 -0
- data/lib/doorkeeper/device_authorization_grant.rb +9 -0
- data/lib/doorkeeper/device_authorization_grant/config.rb +4 -2
- data/lib/doorkeeper/device_authorization_grant/oauth.rb +12 -0
- data/lib/doorkeeper/device_authorization_grant/oauth/device_authorization_request.rb +6 -3
- data/lib/doorkeeper/device_authorization_grant/oauth/device_authorization_response.rb +2 -1
- data/lib/doorkeeper/device_authorization_grant/oauth/device_code_request.rb +6 -6
- data/lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant.rb +1 -116
- data/lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant_mixin.rb +146 -0
- data/lib/doorkeeper/device_authorization_grant/request/device_authorization.rb +3 -2
- data/lib/doorkeeper/device_authorization_grant/version.rb +1 -1
- metadata +39 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a95bd7a093b27e1735e590152c1eb72b0d24ddfe24769ef096bdfed7e5085fe7
|
4
|
+
data.tar.gz: 787d3736c5b2689191d505d67bd997c6ead61a6ca6f5c41233fde9c25d9cbc3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa55bc03f056f52064fd0defd3f8562508e62af6006cf28ba60cb1371a6f89ca1d5109f33a1d36639fdc8fdafecda7c6cf934a04c99ac169d158fc41318f7775
|
7
|
+
data.tar.gz: 335a27953d47a3ee17e6052510f9cfdd75cc55eaaacac1cd0e8b76b1eea8c09a4dbd573ba41ab4d68f0bda34da36a115a0dd2c7601e4640c1171b87e06ce8dc0
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -7,17 +7,6 @@ This library implements the OAuth 2.0 device authorization grant
|
|
7
7
|
[Ruby on Rails](https://rubyonrails.org/) applications on top of the
|
8
8
|
[Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) OAuth 2.0 framework.
|
9
9
|
|
10
|
-
## Status
|
11
|
-
|
12
|
-
This extension currently works with Doorkeeper version `>= 5.4.0`.
|
13
|
-
|
14
|
-
As of June 25 2020, due to some limitations of Doorkeeper, it is currently
|
15
|
-
inconvenient for this Gem to use the official OAuth grant type
|
16
|
-
`urn:ietf:params:oauth:grant-type:device_code`. Instead, it has been renamed
|
17
|
-
to simply `device_code`, which is non-standard. This is going to be corrected
|
18
|
-
soon: the next Doorkeeper release will include the ability to cleanly
|
19
|
-
register custom Grant Flows - see [Doorkeeper Pull Request #1418](https://github.com/doorkeeper-gem/doorkeeper/pull/1418).
|
20
|
-
|
21
10
|
## Installation
|
22
11
|
|
23
12
|
Add this line to your application's Gemfile:
|
@@ -55,7 +44,7 @@ $ rails doorkeeper_device_authorization_grant_engine:install:migrations
|
|
55
44
|
### Doorkeeper configuration
|
56
45
|
|
57
46
|
In your Doorkeeper initializer (usually `config/initializers/doorkeeper.rb`), enable
|
58
|
-
the
|
47
|
+
the new grant flow extension, adding to the `grant_flows` option the `device_code`
|
59
48
|
string. For example:
|
60
49
|
|
61
50
|
```ruby
|
@@ -65,9 +54,6 @@ string. For example:
|
|
65
54
|
# ...
|
66
55
|
|
67
56
|
grant_flows [
|
68
|
-
# Note: this is a non-standard grant flow, used instead of the
|
69
|
-
# official `urn:ietf:params:oauth:grant-type:device_code` due to
|
70
|
-
# current Doorkeeper limitations.
|
71
57
|
'device_code',
|
72
58
|
|
73
59
|
# together with all the other grant flows you already enabled, for example:
|
@@ -80,11 +66,6 @@ string. For example:
|
|
80
66
|
end
|
81
67
|
```
|
82
68
|
|
83
|
-
Please note that **this is not the official grant flow**. The real one should be
|
84
|
-
the IANA URN `urn:ietf:params:oauth:grant-type:device_code`, however this is hard
|
85
|
-
to support with the current version of Doorkeeper, due to how strategy classes are
|
86
|
-
looked up by grant type value.
|
87
|
-
|
88
69
|
### Device Authorization Grant configuration
|
89
70
|
|
90
71
|
The gem's installation scripts automatically creates a new initializer file:
|
@@ -237,15 +218,11 @@ by Dorkeeper), for example:
|
|
237
218
|
POST /oauth/token HTTP/1.1
|
238
219
|
Content-Type: application/x-www-form-urlencoded
|
239
220
|
|
240
|
-
grant_type=device_code
|
221
|
+
grant_type=urn:ietf:params:oauth:grant-type:device_code
|
241
222
|
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
|
242
223
|
&client_id=1406020730
|
243
224
|
```
|
244
225
|
|
245
|
-
**Note:** this is a non-standard `grant_type`, used instead of the official
|
246
|
-
`urn:ietf:params:oauth:grant-type:device_code` due to current limitations of
|
247
|
-
the Doorkeeper gem.
|
248
|
-
|
249
226
|
The response to this request is defined in the next section. It is expected for
|
250
227
|
the *Device Client* to try the access token request repeatedly in a polling
|
251
228
|
fashion, based on the error code in the response. The polling time interval
|
@@ -316,5 +293,12 @@ Content-Type: application/json
|
|
316
293
|
The device authentication flow is now complete, and the token data can be used to
|
317
294
|
authenticate requests against the authorization and/or resource server.
|
318
295
|
|
296
|
+
## Example Application
|
297
|
+
|
298
|
+
Here you can find an example Rails application which uses this gem,
|
299
|
+
together with a little HTML/JS client to try out the device flow:
|
300
|
+
|
301
|
+
[https://github.com/exop-group/doorkeeper-device-flow-example](https://github.com/exop-group/doorkeeper-device-flow-example)
|
302
|
+
|
319
303
|
## License
|
320
304
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,20 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'bundler/setup'
|
5
|
-
rescue LoadError
|
6
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
-
end
|
8
|
-
|
9
|
-
require 'rdoc/task'
|
10
|
-
|
11
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
-
rdoc.rdoc_dir = 'rdoc'
|
13
|
-
rdoc.title = 'Doorkeeper::DeviceAuthorizationGrant'
|
14
|
-
rdoc.options << '--line-numbers'
|
15
|
-
rdoc.rdoc_files.include('README.md')
|
16
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
-
end
|
3
|
+
require 'bundler/setup'
|
18
4
|
|
19
5
|
APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
|
20
6
|
load 'rails/tasks/engine.rake'
|
@@ -9,7 +9,7 @@ module Doorkeeper
|
|
9
9
|
def create
|
10
10
|
headers.merge!(authorize_response.headers)
|
11
11
|
render(json: authorize_response.body, status: authorize_response.status)
|
12
|
-
rescue Errors::DoorkeeperError => e
|
12
|
+
rescue Doorkeeper::Errors::DoorkeeperError => e
|
13
13
|
handle_token_exception(e)
|
14
14
|
end
|
15
15
|
|
data/config/locales/en.yml
CHANGED
@@ -5,11 +5,14 @@ require 'active_model'
|
|
5
5
|
require 'doorkeeper/device_authorization_grant/config'
|
6
6
|
require 'doorkeeper/device_authorization_grant/engine'
|
7
7
|
|
8
|
+
# Doorkeeper namespace
|
8
9
|
module Doorkeeper
|
9
10
|
# OAuth 2.0 Device Authorization Grant extension for Doorkeeper.
|
10
11
|
module DeviceAuthorizationGrant
|
11
12
|
autoload :DeviceGrant, 'doorkeeper/device_authorization_grant/orm/active_record/device_grant'
|
13
|
+
autoload :DeviceGrantMixin, 'doorkeeper/device_authorization_grant/orm/active_record/device_grant_mixin'
|
12
14
|
autoload :Errors, 'doorkeeper/device_authorization_grant/errors'
|
15
|
+
autoload :OAuth, 'doorkeeper/device_authorization_grant/oauth'
|
13
16
|
autoload :VERSION, 'doorkeeper/device_authorization_grant/version'
|
14
17
|
|
15
18
|
# Namespace for device authorization request strategies
|
@@ -44,4 +47,10 @@ module Doorkeeper
|
|
44
47
|
module Request
|
45
48
|
autoload :DeviceCode, 'doorkeeper/request/device_code'
|
46
49
|
end
|
50
|
+
|
51
|
+
Doorkeeper::GrantFlow.register(
|
52
|
+
:device_code,
|
53
|
+
grant_type_matches: Doorkeeper::DeviceAuthorizationGrant::OAuth::DEVICE_CODE,
|
54
|
+
grant_type_strategy: Doorkeeper::Request::DeviceCode
|
55
|
+
)
|
47
56
|
end
|
@@ -18,8 +18,10 @@ module Doorkeeper
|
|
18
18
|
# Error raised in case of missing configuration
|
19
19
|
class MissingConfiguration < StandardError
|
20
20
|
def initialize
|
21
|
-
super(
|
22
|
-
|
21
|
+
super(
|
22
|
+
'Configuration for Doorkeeper::DeviceAuthorizationGrant missing. ' \
|
23
|
+
'Do you have Doorkeeper::DeviceAuthorizationGrant initializer?'
|
24
|
+
)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module DeviceAuthorizationGrant
|
5
|
+
module OAuth
|
6
|
+
# IANA URN of the Device Authorization Grant Type.
|
7
|
+
# @see https://tools.ietf.org/html/rfc8628#section-7.2 RFC 8628 - 7.2. OAuth URI Registration
|
8
|
+
DEVICE_CODE = 'urn:ietf:params:oauth:grant-type:device_code'
|
9
|
+
public_constant :DEVICE_CODE
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -7,8 +7,8 @@ module Doorkeeper
|
|
7
7
|
#
|
8
8
|
# @see https://tools.ietf.org/html/rfc8628#section-3.1 RFC 8628, sect. 3.1
|
9
9
|
class DeviceAuthorizationRequest < Doorkeeper::OAuth::BaseRequest
|
10
|
-
attr_accessor :server
|
11
|
-
|
10
|
+
attr_accessor :server,
|
11
|
+
:client
|
12
12
|
|
13
13
|
# @return [String]
|
14
14
|
attr_accessor :host_name
|
@@ -18,10 +18,13 @@ module Doorkeeper
|
|
18
18
|
# @param server
|
19
19
|
# @param client
|
20
20
|
# @param host_name [String]
|
21
|
-
|
21
|
+
# @param parameters [Hash]
|
22
|
+
def initialize(server, client, host_name, parameters = {}) # rubocop:disable Style/OptionHash
|
23
|
+
super()
|
22
24
|
@server = server
|
23
25
|
@client = client
|
24
26
|
@host_name = host_name
|
27
|
+
@original_scopes = parameters[:scope]
|
25
28
|
end
|
26
29
|
|
27
30
|
# @return [DeviceAuthorizationResponse, Doorkeeper::OAuth::ErrorResponse]
|
@@ -16,6 +16,7 @@ module Doorkeeper
|
|
16
16
|
# @param device_grant [DeviceGrant]
|
17
17
|
# @param host_name [String]
|
18
18
|
def initialize(device_grant, host_name)
|
19
|
+
super()
|
19
20
|
@device_grant = device_grant
|
20
21
|
@host_name = host_name
|
21
22
|
end
|
@@ -28,7 +29,7 @@ module Doorkeeper
|
|
28
29
|
# @return [Hash]
|
29
30
|
def body
|
30
31
|
{
|
31
|
-
'device_code' => device_grant.
|
32
|
+
'device_code' => device_grant.plaintext_device_code,
|
32
33
|
'user_code' => device_grant.user_code,
|
33
34
|
'verification_uri' => verification_uri,
|
34
35
|
'verification_uri_complete' => verification_uri_complete,
|
@@ -7,14 +7,13 @@ module Doorkeeper
|
|
7
7
|
#
|
8
8
|
# @see https://tools.ietf.org/html/rfc8628#section-3.4 RFC 8628, sect. 3.4
|
9
9
|
class DeviceCodeRequest < ::Doorkeeper::OAuth::BaseRequest
|
10
|
-
attr_accessor :server
|
11
|
-
|
10
|
+
attr_accessor :server,
|
11
|
+
:client,
|
12
|
+
:access_token
|
12
13
|
|
13
14
|
# @return [DeviceGrant]
|
14
15
|
attr_accessor :device_grant
|
15
16
|
|
16
|
-
attr_accessor :access_token
|
17
|
-
|
18
17
|
validate :client, error: :invalid_client
|
19
18
|
validate :device_grant, error: :invalid_grant
|
20
19
|
|
@@ -22,12 +21,13 @@ module Doorkeeper
|
|
22
21
|
# @param client
|
23
22
|
# @param device_grant [DeviceGrant]
|
24
23
|
def initialize(server, client, device_grant)
|
24
|
+
super()
|
25
|
+
|
25
26
|
@server = server
|
26
27
|
@client = client
|
27
28
|
@device_grant = device_grant
|
28
29
|
|
29
|
-
|
30
|
-
@grant_type = 'device_code'
|
30
|
+
@grant_type = Doorkeeper::DeviceAuthorizationGrant::OAuth::DEVICE_CODE
|
31
31
|
end
|
32
32
|
|
33
33
|
def before_successful_response
|
@@ -5,41 +5,7 @@ module Doorkeeper
|
|
5
5
|
# Model class, similar to Doorkeeper `AccessGrant`, but specific for
|
6
6
|
# handling OAuth 2.0 Device Authorization Grant.
|
7
7
|
class DeviceGrant < ActiveRecord::Base
|
8
|
-
|
9
|
-
|
10
|
-
include ::Doorkeeper::Models::Expirable
|
11
|
-
|
12
|
-
delegate :secret_strategy, :fallback_secret_strategy, to: :class
|
13
|
-
|
14
|
-
belongs_to :application, class_name: Doorkeeper.configuration.application_class, optional: true
|
15
|
-
|
16
|
-
before_validation :generate_device_code, on: :create
|
17
|
-
|
18
|
-
validates :application_id, presence: true
|
19
|
-
validates :expires_in, presence: true
|
20
|
-
validates :device_code, presence: true, uniqueness: true
|
21
|
-
|
22
|
-
validates :user_code, presence: true, uniqueness: true, if: -> { resource_owner_id.blank? }
|
23
|
-
validates :user_code, absence: true, if: -> { resource_owner_id.present? }
|
24
|
-
|
25
|
-
validates :resource_owner_id, presence: true, if: -> { user_code.blank? }
|
26
|
-
validates :resource_owner_id, absence: true, if: -> { user_code.present? }
|
27
|
-
|
28
|
-
scope(
|
29
|
-
:expired,
|
30
|
-
lambda do
|
31
|
-
exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
|
32
|
-
where('created_at <= :expiration_date', expiration_date: exp_in.seconds.ago)
|
33
|
-
end
|
34
|
-
)
|
35
|
-
|
36
|
-
scope(
|
37
|
-
:unexpired,
|
38
|
-
lambda do
|
39
|
-
exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
|
40
|
-
where('created_at > :expiration_date', expiration_date: exp_in.seconds.ago)
|
41
|
-
end
|
42
|
-
)
|
8
|
+
include DeviceGrantMixin
|
43
9
|
|
44
10
|
# @!attribute application_id
|
45
11
|
# @return [Integer]
|
@@ -64,87 +30,6 @@ module Doorkeeper
|
|
64
30
|
|
65
31
|
# @!attribute last_polling_at
|
66
32
|
# @return [Time, nil]
|
67
|
-
|
68
|
-
class << self
|
69
|
-
# Returns an instance of the DeviceGrant with specific device code
|
70
|
-
# value.
|
71
|
-
#
|
72
|
-
# @param device_code [#to_s] device code value
|
73
|
-
# @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
|
74
|
-
# DeviceGrant object, or nil if there is no record with such code
|
75
|
-
def find_by_plaintext_device_code(device_code)
|
76
|
-
device_code = device_code.to_s
|
77
|
-
|
78
|
-
find_by(device_code: secret_strategy.transform_secret(device_code)) ||
|
79
|
-
find_by_fallback_device_code(device_code)
|
80
|
-
end
|
81
|
-
|
82
|
-
alias by_device_code find_by_plaintext_device_code
|
83
|
-
|
84
|
-
# Allow looking up previously plain device codes as a fallback IFF a
|
85
|
-
# fallback strategy has been defined
|
86
|
-
#
|
87
|
-
# @param plain_secret [#to_s] plain secret value
|
88
|
-
# @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
|
89
|
-
# DeviceGrant object or nil if there is no record with such code
|
90
|
-
def find_by_fallback_device_code(plain_secret)
|
91
|
-
return nil unless fallback_secret_strategy
|
92
|
-
|
93
|
-
# Use the previous strategy to look up
|
94
|
-
stored_code = fallback_secret_strategy.transform_secret(plain_secret)
|
95
|
-
find_by(device_code: stored_code).tap do |resource|
|
96
|
-
upgrade_fallback_value(resource, plain_secret) if resource
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Allows to replace a plain value fallback, to avoid it remaining as
|
101
|
-
# plain text.
|
102
|
-
#
|
103
|
-
# @param instance [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant]
|
104
|
-
# An instance of this model with a plain value device code.
|
105
|
-
# @param plain_secret [String] The plain secret to upgrade.
|
106
|
-
def upgrade_fallback_value(instance, plain_secret)
|
107
|
-
upgraded =
|
108
|
-
secret_strategy.store_secret(instance, :device_code, plain_secret)
|
109
|
-
instance.update(device_code: upgraded)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Determines the secret storing transformer
|
113
|
-
# Unless configured otherwise, uses the plain secret strategy
|
114
|
-
def secret_strategy
|
115
|
-
::Doorkeeper.configuration.token_secret_strategy
|
116
|
-
end
|
117
|
-
|
118
|
-
# Determine the fallback storing strategy
|
119
|
-
# Unless configured, there will be no fallback
|
120
|
-
def fallback_secret_strategy
|
121
|
-
::Doorkeeper.configuration.token_secret_fallback_strategy
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# We keep a volatile copy of the raw device code for initial
|
126
|
-
# communication.
|
127
|
-
#
|
128
|
-
# Some strategies allow restoring stored secrets (e.g. symmetric
|
129
|
-
# encryption) while hashing strategies do not, so you cannot rely on
|
130
|
-
# this value returning a present value for persisted device codes.
|
131
|
-
def plaintext_device_code
|
132
|
-
if secret_strategy.allows_restoring_secrets?
|
133
|
-
secret_strategy.restore_secret(self, :device_code)
|
134
|
-
else
|
135
|
-
@raw_device_code
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
# Generates a device code value with UniqueToken class.
|
142
|
-
#
|
143
|
-
# @return [String] device code value
|
144
|
-
def generate_device_code
|
145
|
-
@raw_device_code = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
146
|
-
secret_strategy.store_secret(self, :device_code, @raw_device_code)
|
147
|
-
end
|
148
33
|
end
|
149
34
|
end
|
150
35
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module DeviceAuthorizationGrant
|
5
|
+
# Module mixin for Device Grant models.
|
6
|
+
#
|
7
|
+
# This is similar to Doorkeeper `AccessGrantMixin`, but specific for handling
|
8
|
+
# OAuth 2.0 Device Authorization Grant.
|
9
|
+
module DeviceGrantMixin
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
include ::Doorkeeper::Models::Expirable
|
12
|
+
|
13
|
+
included do # rubocop:disable Metrics/BlockLength
|
14
|
+
self.table_name = "#{table_name_prefix}oauth_device_grants#{table_name_suffix}"
|
15
|
+
|
16
|
+
delegate :secret_strategy, :fallback_secret_strategy, to: :class
|
17
|
+
|
18
|
+
belongs_to :application, class_name: Doorkeeper.configuration.application_class, optional: true
|
19
|
+
|
20
|
+
before_validation :generate_device_code, on: :create
|
21
|
+
|
22
|
+
validates :application_id, presence: true
|
23
|
+
validates :expires_in, presence: true
|
24
|
+
validates :device_code, presence: true, uniqueness: true
|
25
|
+
|
26
|
+
validates :user_code, presence: true, uniqueness: true, if: -> { resource_owner_id.blank? }
|
27
|
+
validates :user_code, absence: true, if: -> { resource_owner_id.present? }
|
28
|
+
|
29
|
+
validates :resource_owner_id, presence: true, if: -> { user_code.blank? }
|
30
|
+
validates :resource_owner_id, absence: true, if: -> { user_code.present? }
|
31
|
+
validate :scopes_match_configured, if: :enforce_scopes?
|
32
|
+
|
33
|
+
scope(
|
34
|
+
:expired,
|
35
|
+
lambda do
|
36
|
+
exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
|
37
|
+
where('created_at <= :expiration_date', expiration_date: exp_in.seconds.ago)
|
38
|
+
end
|
39
|
+
)
|
40
|
+
|
41
|
+
scope(
|
42
|
+
:unexpired,
|
43
|
+
lambda do
|
44
|
+
exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
|
45
|
+
where('created_at > :expiration_date', expiration_date: exp_in.seconds.ago)
|
46
|
+
end
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# ClassMethods
|
51
|
+
module ClassMethods
|
52
|
+
# Returns an instance of the DeviceGrant with specific device code
|
53
|
+
# value.
|
54
|
+
#
|
55
|
+
# @param device_code [#to_s] device code value
|
56
|
+
# @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
|
57
|
+
# DeviceGrant object, or nil if there is no record with such code
|
58
|
+
def find_by_plaintext_device_code(device_code)
|
59
|
+
device_code = device_code.to_s
|
60
|
+
|
61
|
+
find_by(device_code: secret_strategy.transform_secret(device_code)) ||
|
62
|
+
find_by_fallback_device_code(device_code)
|
63
|
+
end
|
64
|
+
|
65
|
+
alias by_device_code find_by_plaintext_device_code
|
66
|
+
|
67
|
+
# Allow looking up previously plain device codes as a fallback IFF a
|
68
|
+
# fallback strategy has been defined
|
69
|
+
#
|
70
|
+
# @param plain_secret [#to_s] plain secret value
|
71
|
+
# @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
|
72
|
+
# DeviceGrant object or nil if there is no record with such code
|
73
|
+
def find_by_fallback_device_code(plain_secret)
|
74
|
+
return nil unless fallback_secret_strategy
|
75
|
+
|
76
|
+
# Use the previous strategy to look up
|
77
|
+
stored_code = fallback_secret_strategy.transform_secret(plain_secret)
|
78
|
+
find_by(device_code: stored_code).tap do |resource|
|
79
|
+
upgrade_fallback_value(resource, plain_secret) if resource
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Allows to replace a plain value fallback, to avoid it remaining as
|
84
|
+
# plain text.
|
85
|
+
#
|
86
|
+
# @param instance [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant]
|
87
|
+
# An instance of this model with a plain value device code.
|
88
|
+
# @param plain_secret [String] The plain secret to upgrade.
|
89
|
+
def upgrade_fallback_value(instance, plain_secret)
|
90
|
+
upgraded =
|
91
|
+
secret_strategy.store_secret(instance, :device_code, plain_secret)
|
92
|
+
instance.update(device_code: upgraded)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Determines the secret storing transformer
|
96
|
+
# Unless configured otherwise, uses the plain secret strategy
|
97
|
+
def secret_strategy
|
98
|
+
::Doorkeeper.configuration.token_secret_strategy
|
99
|
+
end
|
100
|
+
|
101
|
+
# Determine the fallback storing strategy
|
102
|
+
# Unless configured, there will be no fallback
|
103
|
+
def fallback_secret_strategy
|
104
|
+
::Doorkeeper.configuration.token_secret_fallback_strategy
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# We keep a volatile copy of the raw device code for initial
|
109
|
+
# communication.
|
110
|
+
#
|
111
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric
|
112
|
+
# encryption) while hashing strategies do not, so you cannot rely on
|
113
|
+
# this value returning a present value for persisted device codes.
|
114
|
+
def plaintext_device_code
|
115
|
+
if secret_strategy.allows_restoring_secrets?
|
116
|
+
secret_strategy.restore_secret(self, :device_code)
|
117
|
+
else
|
118
|
+
@raw_device_code
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Generates a device code value with UniqueToken class.
|
125
|
+
#
|
126
|
+
# @return [String] device code value
|
127
|
+
def generate_device_code
|
128
|
+
@raw_device_code = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
129
|
+
secret_strategy.store_secret(self, :device_code, @raw_device_code)
|
130
|
+
end
|
131
|
+
|
132
|
+
def scopes_match_configured
|
133
|
+
if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
134
|
+
scope_str: scopes.to_s,
|
135
|
+
server_scopes: Doorkeeper.config.scopes
|
136
|
+
)
|
137
|
+
errors.add(:scopes, :not_match_configured)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def enforce_scopes?
|
142
|
+
Doorkeeper.config.enforce_configured_scopes?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -7,14 +7,15 @@ module Doorkeeper
|
|
7
7
|
#
|
8
8
|
# @see https://tools.ietf.org/html/rfc8628#section-3.1 RFC 8628, sect. 3.1
|
9
9
|
class DeviceAuthorization < ::Doorkeeper::Request::Strategy
|
10
|
-
delegate :client, to: :server
|
10
|
+
delegate :client, :parameters, to: :server
|
11
11
|
|
12
12
|
# @return [OAuth::DeviceAuthorizationRequest]
|
13
13
|
def request
|
14
14
|
@request ||= OAuth::DeviceAuthorizationRequest.new(
|
15
15
|
Doorkeeper.configuration,
|
16
16
|
client,
|
17
|
-
host_name
|
17
|
+
host_name,
|
18
|
+
parameters
|
18
19
|
)
|
19
20
|
end
|
20
21
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doorkeeper-device_authorization_grant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- EXOP Group
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: doorkeeper
|
@@ -16,70 +16,90 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '5.
|
19
|
+
version: '5.5'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '5.
|
26
|
+
version: '5.5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '1.14'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '1.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.10'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.10.1
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '2.10'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.10.1
|
41
61
|
- !ruby/object:Gem::Dependency
|
42
62
|
name: simplecov
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
44
64
|
requirements:
|
45
65
|
- - "~>"
|
46
66
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
67
|
+
version: 0.21.2
|
48
68
|
type: :development
|
49
69
|
prerelease: false
|
50
70
|
version_requirements: !ruby/object:Gem::Requirement
|
51
71
|
requirements:
|
52
72
|
- - "~>"
|
53
73
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
74
|
+
version: 0.21.2
|
55
75
|
- !ruby/object:Gem::Dependency
|
56
76
|
name: sqlite3
|
57
77
|
requirement: !ruby/object:Gem::Requirement
|
58
78
|
requirements:
|
59
79
|
- - "~>"
|
60
80
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
81
|
+
version: 1.4.2
|
62
82
|
type: :development
|
63
83
|
prerelease: false
|
64
84
|
version_requirements: !ruby/object:Gem::Requirement
|
65
85
|
requirements:
|
66
86
|
- - "~>"
|
67
87
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
88
|
+
version: 1.4.2
|
69
89
|
- !ruby/object:Gem::Dependency
|
70
90
|
name: yard
|
71
91
|
requirement: !ruby/object:Gem::Requirement
|
72
92
|
requirements:
|
73
93
|
- - "~>"
|
74
94
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.9.
|
95
|
+
version: 0.9.26
|
76
96
|
type: :development
|
77
97
|
prerelease: false
|
78
98
|
version_requirements: !ruby/object:Gem::Requirement
|
79
99
|
requirements:
|
80
100
|
- - "~>"
|
81
101
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.9.
|
102
|
+
version: 0.9.26
|
83
103
|
description: OAuth 2.0 Device Authorization Grant extension for Doorkeeper.
|
84
104
|
email:
|
85
105
|
- opensource@exop-group.com
|
@@ -99,12 +119,14 @@ files:
|
|
99
119
|
- lib/doorkeeper/device_authorization_grant/config.rb
|
100
120
|
- lib/doorkeeper/device_authorization_grant/engine.rb
|
101
121
|
- lib/doorkeeper/device_authorization_grant/errors.rb
|
122
|
+
- lib/doorkeeper/device_authorization_grant/oauth.rb
|
102
123
|
- lib/doorkeeper/device_authorization_grant/oauth/device_authorization_request.rb
|
103
124
|
- lib/doorkeeper/device_authorization_grant/oauth/device_authorization_response.rb
|
104
125
|
- lib/doorkeeper/device_authorization_grant/oauth/device_code_request.rb
|
105
126
|
- lib/doorkeeper/device_authorization_grant/oauth/helpers/user_code.rb
|
106
127
|
- lib/doorkeeper/device_authorization_grant/orm/active_record.rb
|
107
128
|
- lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant.rb
|
129
|
+
- lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant_mixin.rb
|
108
130
|
- lib/doorkeeper/device_authorization_grant/rails/routes.rb
|
109
131
|
- lib/doorkeeper/device_authorization_grant/rails/routes/mapper.rb
|
110
132
|
- lib/doorkeeper/device_authorization_grant/rails/routes/mapping.rb
|
@@ -116,7 +138,10 @@ files:
|
|
116
138
|
homepage: https://github.com/exop-group/doorkeeper-device_authorization_grant
|
117
139
|
licenses:
|
118
140
|
- MIT
|
119
|
-
metadata:
|
141
|
+
metadata:
|
142
|
+
homepage_uri: https://github.com/exop-group/doorkeeper-device_authorization_grant
|
143
|
+
source_code_uri: https://github.com/exop-group/doorkeeper-device_authorization_grant
|
144
|
+
changelog_uri: https://github.com/exop-group/doorkeeper-device_authorization_grant/blob/master/CHANGELOG.md
|
120
145
|
post_install_message:
|
121
146
|
rdoc_options: []
|
122
147
|
require_paths:
|
@@ -132,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
157
|
- !ruby/object:Gem::Version
|
133
158
|
version: '0'
|
134
159
|
requirements: []
|
135
|
-
rubygems_version: 3.
|
160
|
+
rubygems_version: 3.0.8
|
136
161
|
signing_key:
|
137
162
|
specification_version: 4
|
138
163
|
summary: OAuth 2.0 Device Authorization Grant extension for Doorkeeper.
|