doorkeeper-device_authorization_grant 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5231c51f4fc5f93284e30b08cc6188e4ac43cfecfd1908110597350ddd0c2cf7
4
- data.tar.gz: 8cc73d6fe81263c383290e7c2b68a477b6cd0f4b6c9873d9efcdd4493fc1242b
3
+ metadata.gz: '08caf432de1258cd6b0dab0346a332f0a49698e35838194e1e6e9f999d1a2ccc'
4
+ data.tar.gz: 98c6b042087cb1a1df1f84d4e9aca7327f2cb637eaee58729de53a0d76fd9900
5
5
  SHA512:
6
- metadata.gz: 8f67e3f89aad7ce4cfc2ac7d7c70d1dcb63b6129bb877592fe14e917c10cea626c5cabd3724fe427415215da4a965d81c042de7f615f861c76a32cb5a95272f1
7
- data.tar.gz: e4fefdce2c1662f05f48871c47927ddd46607de767652273e73c98120d4734cd2e1cb79c2ec045cd1536948d4584b475f57060124711ec0cffbb82bd81b69864
6
+ metadata.gz: 05dca92fad2e8ef88aae7d9739aa08c990dd5643c1ef4aab563b761d3afb1f08727efd2f508d45155e524943f9082203fa8e7019bd0829867a0c218266aa9fb8
7
+ data.tar.gz: c603c13d33a65616bd370dac83625396318ee7e47e0dc16f99cd620c23ebd5e1df0f9a30a6b7915eb33f85dd9ba7e2d2284793c0156808c86350591e97ae9085
data/README.md CHANGED
@@ -316,5 +316,12 @@ Content-Type: application/json
316
316
  The device authentication flow is now complete, and the token data can be used to
317
317
  authenticate requests against the authorization and/or resource server.
318
318
 
319
+ ## Example Application
320
+
321
+ Here you can find an example Rails application which uses this gem,
322
+ together with a little HTML/JS client to try out the device flow:
323
+
324
+ [https://github.com/exop-group/doorkeeper-device-flow-example](https://github.com/exop-group/doorkeeper-device-flow-example)
325
+
319
326
  ## License
320
327
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -9,6 +9,7 @@ module Doorkeeper
9
9
  # OAuth 2.0 Device Authorization Grant extension for Doorkeeper.
10
10
  module DeviceAuthorizationGrant
11
11
  autoload :DeviceGrant, 'doorkeeper/device_authorization_grant/orm/active_record/device_grant'
12
+ autoload :DeviceGrantMixin, 'doorkeeper/device_authorization_grant/orm/active_record/device_grant_mixin'
12
13
  autoload :Errors, 'doorkeeper/device_authorization_grant/errors'
13
14
  autoload :VERSION, 'doorkeeper/device_authorization_grant/version'
14
15
 
@@ -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('Configuration for Doorkeeper::DeviceAuthorizationGrant missing. ' \
22
- 'Do you have Doorkeeper::DeviceAuthorizationGrant initializer?')
21
+ super(
22
+ 'Configuration for Doorkeeper::DeviceAuthorizationGrant missing. ' \
23
+ 'Do you have Doorkeeper::DeviceAuthorizationGrant initializer?'
24
+ )
23
25
  end
24
26
  end
25
27
 
@@ -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
- attr_accessor :client
10
+ attr_accessor :server,
11
+ :client
12
12
 
13
13
  # @return [String]
14
14
  attr_accessor :host_name
@@ -19,6 +19,7 @@ module Doorkeeper
19
19
  # @param client
20
20
  # @param host_name [String]
21
21
  def initialize(server, client, host_name)
22
+ super()
22
23
  @server = server
23
24
  @client = client
24
25
  @host_name = host_name
@@ -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
@@ -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
- attr_accessor :client
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,6 +21,8 @@ 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
@@ -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
- self.table_name = "#{table_name_prefix}oauth_device_grants#{table_name_suffix}"
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,132 @@
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
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
+
32
+ scope(
33
+ :expired,
34
+ lambda do
35
+ exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
36
+ where('created_at <= :expiration_date', expiration_date: exp_in.seconds.ago)
37
+ end
38
+ )
39
+
40
+ scope(
41
+ :unexpired,
42
+ lambda do
43
+ exp_in = DeviceAuthorizationGrant.configuration.device_code_expires_in
44
+ where('created_at > :expiration_date', expiration_date: exp_in.seconds.ago)
45
+ end
46
+ )
47
+ end
48
+
49
+ # ClassMethods
50
+ module ClassMethods
51
+ # Returns an instance of the DeviceGrant with specific device code
52
+ # value.
53
+ #
54
+ # @param device_code [#to_s] device code value
55
+ # @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
56
+ # DeviceGrant object, or nil if there is no record with such code
57
+ def find_by_plaintext_device_code(device_code)
58
+ device_code = device_code.to_s
59
+
60
+ find_by(device_code: secret_strategy.transform_secret(device_code)) ||
61
+ find_by_fallback_device_code(device_code)
62
+ end
63
+
64
+ alias by_device_code find_by_plaintext_device_code
65
+
66
+ # Allow looking up previously plain device codes as a fallback IFF a
67
+ # fallback strategy has been defined
68
+ #
69
+ # @param plain_secret [#to_s] plain secret value
70
+ # @return [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant, nil]
71
+ # DeviceGrant object or nil if there is no record with such code
72
+ def find_by_fallback_device_code(plain_secret)
73
+ return nil unless fallback_secret_strategy
74
+
75
+ # Use the previous strategy to look up
76
+ stored_code = fallback_secret_strategy.transform_secret(plain_secret)
77
+ find_by(device_code: stored_code).tap do |resource|
78
+ upgrade_fallback_value(resource, plain_secret) if resource
79
+ end
80
+ end
81
+
82
+ # Allows to replace a plain value fallback, to avoid it remaining as
83
+ # plain text.
84
+ #
85
+ # @param instance [Doorkeeper::DeviceAuthorizationGrant::DeviceGrant]
86
+ # An instance of this model with a plain value device code.
87
+ # @param plain_secret [String] The plain secret to upgrade.
88
+ def upgrade_fallback_value(instance, plain_secret)
89
+ upgraded =
90
+ secret_strategy.store_secret(instance, :device_code, plain_secret)
91
+ instance.update(device_code: upgraded)
92
+ end
93
+
94
+ # Determines the secret storing transformer
95
+ # Unless configured otherwise, uses the plain secret strategy
96
+ def secret_strategy
97
+ ::Doorkeeper.configuration.token_secret_strategy
98
+ end
99
+
100
+ # Determine the fallback storing strategy
101
+ # Unless configured, there will be no fallback
102
+ def fallback_secret_strategy
103
+ ::Doorkeeper.configuration.token_secret_fallback_strategy
104
+ end
105
+ end
106
+
107
+ # We keep a volatile copy of the raw device code for initial
108
+ # communication.
109
+ #
110
+ # Some strategies allow restoring stored secrets (e.g. symmetric
111
+ # encryption) while hashing strategies do not, so you cannot rely on
112
+ # this value returning a present value for persisted device codes.
113
+ def plaintext_device_code
114
+ if secret_strategy.allows_restoring_secrets?
115
+ secret_strategy.restore_secret(self, :device_code)
116
+ else
117
+ @raw_device_code
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ # Generates a device code value with UniqueToken class.
124
+ #
125
+ # @return [String] device code value
126
+ def generate_device_code
127
+ @raw_device_code = Doorkeeper::OAuth::Helpers::UniqueToken.generate
128
+ secret_strategy.store_secret(self, :device_code, @raw_device_code)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doorkeeper
4
4
  module DeviceAuthorizationGrant
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.0'
6
6
  public_constant :VERSION
7
7
  end
8
8
  end
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.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - EXOP Group
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-30 00:00:00.000000000 Z
11
+ date: 2021-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: doorkeeper
@@ -30,56 +30,70 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.86.0
33
+ version: '1.8'
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: 0.86.0
40
+ version: '1.8'
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.9.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.9.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: simplecov
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 0.18.5
61
+ version: 0.21.1
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 0.18.5
68
+ version: 0.21.1
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: sqlite3
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '1.3'
75
+ version: 1.4.2
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '1.3'
82
+ version: 1.4.2
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: yard
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 0.9.25
89
+ version: 0.9.26
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 0.9.25
96
+ version: 0.9.26
83
97
  description: OAuth 2.0 Device Authorization Grant extension for Doorkeeper.
84
98
  email:
85
99
  - opensource@exop-group.com
@@ -105,6 +119,7 @@ files:
105
119
  - lib/doorkeeper/device_authorization_grant/oauth/helpers/user_code.rb
106
120
  - lib/doorkeeper/device_authorization_grant/orm/active_record.rb
107
121
  - lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant.rb
122
+ - lib/doorkeeper/device_authorization_grant/orm/active_record/device_grant_mixin.rb
108
123
  - lib/doorkeeper/device_authorization_grant/rails/routes.rb
109
124
  - lib/doorkeeper/device_authorization_grant/rails/routes/mapper.rb
110
125
  - lib/doorkeeper/device_authorization_grant/rails/routes/mapping.rb