doorkeeper-device_authorization_grant 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -0
- data/lib/doorkeeper/device_authorization_grant.rb +1 -0
- data/lib/doorkeeper/device_authorization_grant/config.rb +4 -2
- data/lib/doorkeeper/device_authorization_grant/oauth/device_authorization_request.rb +3 -2
- data/lib/doorkeeper/device_authorization_grant/oauth/device_authorization_response.rb +1 -0
- data/lib/doorkeeper/device_authorization_grant/oauth/device_code_request.rb +5 -4
- 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 +132 -0
- data/lib/doorkeeper/device_authorization_grant/version.rb +1 -1
- metadata +25 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08caf432de1258cd6b0dab0346a332f0a49698e35838194e1e6e9f999d1a2ccc'
|
4
|
+
data.tar.gz: 98c6b042087cb1a1df1f84d4e9aca7327f2cb637eaee58729de53a0d76fd9900
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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
|
|
@@ -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
|
@@ -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
|
@@ -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,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
|
-
|
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
|
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.
|
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:
|
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:
|
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:
|
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.
|
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.
|
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:
|
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:
|
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.
|
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.
|
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
|