doorkeeper-device_authorization_grant 0.1.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|