dpop 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +80 -0
- data/README.md +97 -6
- data/lib/dpop/configuration.rb +24 -0
- data/lib/dpop/controller.rb +52 -0
- data/lib/dpop/cookie_jar.rb +42 -0
- data/lib/dpop/encryptor.rb +33 -0
- data/lib/dpop/key_generator.rb +22 -0
- data/lib/dpop/proof_generator.rb +79 -0
- data/lib/dpop/railtie.rb +10 -0
- data/lib/dpop/version.rb +1 -1
- data/lib/dpop.rb +46 -2
- metadata +128 -7
- data/sig/dpop.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 515c773b21830f39448b7cf146d976eb66c0121efb97a316063b9643d00589fb
|
4
|
+
data.tar.gz: 7c8e28c5dde868992435cd6e2df6c5801d809d1e3124b0ec2101b1f1ee7e7a30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c83c6fd46e8290ef434c7869b2fd3096771e829119308d2ff096ebfe6227df24ac2a2b64cef7f58a45a1fa2ba836e6abadc46f4c5067509fb3640c3d96bd929
|
7
|
+
data.tar.gz: c12968b246fb12df9a0e82318d799e48c11ea48df8ec1480e0ecbbe11ff4c78c39c270a11de70393820e5c8fd38fc1546bd76b7b5414ed120c95bb2e83b406d2
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rails
|
3
|
+
|
4
|
+
Style/StringLiterals:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/ParameterLists:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Naming/FileName:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Naming/MethodParameterName:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Rails/RakeEnvironment:
|
20
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dpop (0.1.0)
|
5
|
+
activesupport
|
6
|
+
jwt
|
7
|
+
openssl
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (7.0.3.1)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
ast (2.4.2)
|
18
|
+
concurrent-ruby (1.1.10)
|
19
|
+
diff-lcs (1.5.0)
|
20
|
+
i18n (1.12.0)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
jwt (2.4.1)
|
23
|
+
minitest (5.16.2)
|
24
|
+
openssl (3.0.0)
|
25
|
+
parallel (1.22.1)
|
26
|
+
parser (3.1.2.0)
|
27
|
+
ast (~> 2.4.1)
|
28
|
+
rack (2.2.3)
|
29
|
+
rainbow (3.1.1)
|
30
|
+
rake (13.0.6)
|
31
|
+
regexp_parser (2.3.1)
|
32
|
+
rexml (3.2.5)
|
33
|
+
rspec (3.11.0)
|
34
|
+
rspec-core (~> 3.11.0)
|
35
|
+
rspec-expectations (~> 3.11.0)
|
36
|
+
rspec-mocks (~> 3.11.0)
|
37
|
+
rspec-core (3.11.0)
|
38
|
+
rspec-support (~> 3.11.0)
|
39
|
+
rspec-expectations (3.11.0)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.11.0)
|
42
|
+
rspec-mocks (3.11.1)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.11.0)
|
45
|
+
rspec-support (3.11.0)
|
46
|
+
rubocop (1.28.2)
|
47
|
+
parallel (~> 1.10)
|
48
|
+
parser (>= 3.1.0.0)
|
49
|
+
rainbow (>= 2.2.2, < 4.0)
|
50
|
+
regexp_parser (>= 1.8, < 3.0)
|
51
|
+
rexml
|
52
|
+
rubocop-ast (>= 1.17.0, < 2.0)
|
53
|
+
ruby-progressbar (~> 1.7)
|
54
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
55
|
+
rubocop-ast (1.17.0)
|
56
|
+
parser (>= 3.1.1.0)
|
57
|
+
rubocop-rails (2.14.2)
|
58
|
+
activesupport (>= 4.2.0)
|
59
|
+
rack (>= 1.1)
|
60
|
+
rubocop (>= 1.7.0, < 2.0)
|
61
|
+
ruby-progressbar (1.11.0)
|
62
|
+
tzinfo (2.0.5)
|
63
|
+
concurrent-ruby (~> 1.0)
|
64
|
+
unicode-display_width (2.1.0)
|
65
|
+
|
66
|
+
PLATFORMS
|
67
|
+
ruby
|
68
|
+
x86_64-darwin-19
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
bundler
|
72
|
+
dpop!
|
73
|
+
jwt
|
74
|
+
rake (~> 13.0)
|
75
|
+
rspec
|
76
|
+
rubocop
|
77
|
+
rubocop-rails
|
78
|
+
|
79
|
+
BUNDLED WITH
|
80
|
+
2.3.15
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Dpop
|
2
2
|
|
3
|
-
|
3
|
+
Implementation of DPoP ([Demonstrating Proof-of-Possession at the Application Layer](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop)) for Ruby and Rails apps.
|
4
4
|
|
5
|
-
|
5
|
+
Adds a
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -16,14 +16,105 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
|
19
|
+
In general, this gem provides two concepts: A wrapper for creating private keys that this gem can consume, and an API for consuming those private keys to generate Proof JWT's for a given request.
|
20
|
+
|
21
|
+
### Setup
|
22
|
+
|
23
|
+
This gem uses a configuration concept for setup, and then can be accessed through module methods on `Dpop`
|
24
|
+
|
25
|
+
Run the configurer to set with defaults:
|
26
|
+
```ruby
|
27
|
+
Dpop.configure
|
28
|
+
```
|
29
|
+
|
30
|
+
Or pass a block to overwrite defaults:
|
31
|
+
```ruby
|
32
|
+
Dpop.configure do |config|
|
33
|
+
config.encryption_key = MY_SECURE_SECRET_PASSPHRASE
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
|Configurable variable|Description|Default value|
|
38
|
+
|===|===|===|
|
39
|
+
|cookie_name|Cookie saved on the browser when using the Rails controller concern|"_proof_keys"|
|
40
|
+
|encryption_key|Secure passphrase used for encrypting cookes with Rails|ENV["DPOP_ENCRYPTION_KEY"]|
|
41
|
+
|generated_key_size|Byte size of generated private keys|1024|
|
42
|
+
|
43
|
+
|
44
|
+
### Module methods
|
45
|
+
|
46
|
+
To generate a consumable private key, run `Dpop.generate_key_pair`. That will return a PEM string, which can be used with openssl by running `OpenSSL::PKey::RSA.new(key)`
|
47
|
+
|
48
|
+
To generate a Proof JWT, run `Dpop.get_proof_with_key(key, htu: "https://www.website.call/path", htm: "GET"`. That will return a JWT string, which should be added to the Dpop header of your http request.
|
49
|
+
|
50
|
+
### Rails
|
51
|
+
|
52
|
+
In Rails apps, this gem automatically configures on initialization using a Railtie. At any time, those configuration variables can be overwritten manually as described above, but running `Dpop.configure` to initialize the gem isn't mandatory.
|
53
|
+
|
54
|
+
The app provides a controller concern that can be used to ensure user's browsers have a private key saved in their cookies, which can be relied on to prove possession of their browser. A proof can then be generated using that key, to be attached to your HTTP requests.
|
55
|
+
|
56
|
+
In your `ApplicationController`, add `include Dpop::Controller`.
|
57
|
+
|
58
|
+
In your controllers where you'd like to ensure your user has a key set, or in your `ApplicationController`, add `ensure_dpop!`
|
59
|
+
|
60
|
+
When you want to create a proof signed with that key, use `get_proof(htu: "https://www.website.call/path", htm: "GET")`
|
61
|
+
|
62
|
+
Example using `Net::HTTP`:
|
63
|
+
```
|
64
|
+
class MyController < ApplicationController
|
65
|
+
ensure_dpop!
|
66
|
+
|
67
|
+
def index
|
68
|
+
uri = URI("https://www.myresourcehost.com/index?page=1")
|
69
|
+
proof = get_proof(htu: dpop_htu(uri), htm: "GET")
|
70
|
+
|
71
|
+
req = Net::HTTP::Get.new(uri)
|
72
|
+
req['authorization'] = "DPoP #{my_access_token}"
|
73
|
+
req['dpop'] = proof
|
74
|
+
|
75
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
76
|
+
http.request(req)
|
77
|
+
end
|
78
|
+
|
79
|
+
@data = res.body
|
80
|
+
end
|
81
|
+
|
82
|
+
def create
|
83
|
+
uri = URI("https://www.myresourcehost.com/index?page=1")
|
84
|
+
proof = get_proof(htu: dpop_htu(uri), htm: "GET")
|
85
|
+
|
86
|
+
req = Net::HTTP::Post.new(uri)
|
87
|
+
req['authorization'] = "DPoP #{my_access_token}"
|
88
|
+
req['dpop'] = proof
|
89
|
+
|
90
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
91
|
+
http.request(req)
|
92
|
+
end
|
93
|
+
|
94
|
+
@data = res.body
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Only pass scheme, host, and path following DPoP spec
|
100
|
+
def dpop_htu(uri)
|
101
|
+
uri.fragment = dpop_uri.query = nil
|
102
|
+
uri
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
```
|
20
107
|
|
21
108
|
## Development
|
22
109
|
|
23
|
-
|
110
|
+
`bundle install` to setup. Develop using `bundle console` for ruby, or by installing the gem through `path:` in your consuming app.
|
24
111
|
|
25
|
-
To
|
112
|
+
To release a new version, update the version number in `version.rb`, run `bundle install` to update Gemfile.lock, and add a CHANGELOG.md entry for your version.
|
26
113
|
|
27
114
|
## Contributing
|
28
115
|
|
29
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
116
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/WilliamNHarvey/dpop-ruby. Please add clear testing instructions in your PR.
|
117
|
+
|
118
|
+
## License
|
119
|
+
|
120
|
+
[Apache Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Configures the app's module methods
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :cookie_name, :encryption_key, :generated_key_size, :key_alg
|
7
|
+
|
8
|
+
DEFAULT_COOKIE_NAME = "_proof_keys"
|
9
|
+
DEFAULT_ENCRYPTION_KEY = ENV["DPOP_ENCRYPTION_KEY"] || ""
|
10
|
+
DEFAULT_GENERATED_KEY_SIZE = 1024
|
11
|
+
DEFAULT_KEY_ALG = :rsa
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.cookie_name = DEFAULT_COOKIE_NAME
|
15
|
+
self.encryption_key = DEFAULT_ENCRYPTION_KEY
|
16
|
+
self.generated_key_size = DEFAULT_GENERATED_KEY_SIZE
|
17
|
+
self.key_alg = DEFAULT_KEY_ALG
|
18
|
+
end
|
19
|
+
|
20
|
+
def encryptor
|
21
|
+
@encryptor ||= Dpop::Encryptor.new(encryption_key)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Controller concern for Rails
|
5
|
+
module Controller
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Error for DPoP cookie not being found
|
9
|
+
class MissingDpopCookie < StandardError
|
10
|
+
def initialize(cookie_name)
|
11
|
+
super("No DPoP cookie found with name `#{cookie_name}`. Try running `ensure_dpop!` before using this concern.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
included do
|
16
|
+
class_attribute :ensure_dpop_on_actions
|
17
|
+
self.ensure_dpop_on_actions = false
|
18
|
+
|
19
|
+
# Set up the around_action which ensures the cookie is set on request if the controller has ensure_dpop! set
|
20
|
+
before_action :set_dpop_cookie
|
21
|
+
end
|
22
|
+
|
23
|
+
class_methods do
|
24
|
+
def ensure_dpop!
|
25
|
+
self.ensure_dpop_on_actions = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_proof(**args)
|
30
|
+
dpop_key = cookie_jar[Dpop.config.cookie_name]
|
31
|
+
raise MissingDpopCookie, Dpop.config.cookie_name unless dpop_key
|
32
|
+
|
33
|
+
generator = Dpop::ProofGenerator.new(dpop_key, "RS256")
|
34
|
+
generator.create_dpop_proof(args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_dpop_cookie
|
38
|
+
return unless ensure_dpop_on_actions
|
39
|
+
return if cookie_jar[Dpop.config.cookie_name]
|
40
|
+
|
41
|
+
generated = Dpop::KeyGenerator.generate(Dpop.config.key_alg)
|
42
|
+
|
43
|
+
cookie_jar[Dpop.config.cookie_name] = generated
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def cookie_jar
|
49
|
+
Dpop::CookieJar.new(Dpop.config.encryptor, request.cookies)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Manages browser cookies
|
5
|
+
class CookieJar
|
6
|
+
# Error for when cookie is not decipherable
|
7
|
+
class InvalidCookieError < StandardError
|
8
|
+
def initialize(cookie_name, cookie_value)
|
9
|
+
super("invalid value for #{cookie_name}: #{cookie_value}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(encryptor, request_cookies)
|
14
|
+
@encryptor = encryptor
|
15
|
+
@request_cookies = request_cookies
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](cookie_name)
|
19
|
+
try_decrypt(cookie_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(cookie_name, value)
|
23
|
+
encrypted_value = @encryptor.encrypt_and_sign(value)
|
24
|
+
@request_cookies[cookie_name] = encrypted_value
|
25
|
+
end
|
26
|
+
|
27
|
+
def key?(cookie_name)
|
28
|
+
@request_cookies.key?(cookie_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def try_decrypt(cookie_name)
|
34
|
+
value = @request_cookies[cookie_name]
|
35
|
+
return nil if value.blank?
|
36
|
+
|
37
|
+
@encryptor.decrypt_and_verify(value)
|
38
|
+
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
39
|
+
raise InvalidCookieError.new(cookie_name, value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Encrypts and decrypts messages
|
5
|
+
class Encryptor
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
SECRET = "dpop encrypted message"
|
9
|
+
SIGN_SECRET = "signed dpop encrypted message"
|
10
|
+
CIPHER = "aes-256-gcm"
|
11
|
+
|
12
|
+
def_delegators :@message_encryptor, :encrypt_and_sign, :decrypt_and_verify
|
13
|
+
|
14
|
+
def initialize(secret)
|
15
|
+
@message_encryptor = build_message_encryptor(secret)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_message_encryptor(secret)
|
21
|
+
key_generator = ActiveSupport::CachingKeyGenerator.new(
|
22
|
+
ActiveSupport::KeyGenerator.new(
|
23
|
+
secret,
|
24
|
+
iterations: 1000,
|
25
|
+
hash_digest_class: OpenSSL::Digest::SHA256
|
26
|
+
)
|
27
|
+
)
|
28
|
+
secret = key_generator.generate_key(SECRET)[0, 32]
|
29
|
+
sign_secret = key_generator.generate_key(SIGN_SECRET)
|
30
|
+
ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON, cipher: CIPHER)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Generates private keys
|
5
|
+
module KeyGenerator
|
6
|
+
# Error for receiving an unrecognized algorithm
|
7
|
+
class UnsupportedAlgorithmError < StandardError
|
8
|
+
def initialize(alg)
|
9
|
+
super("Unsupported algorithm received: #{alg}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate(alg = :rsa)
|
14
|
+
# TODO: support more algs
|
15
|
+
raise UnsupportedAlgorithmError, alg if alg != :rsa
|
16
|
+
|
17
|
+
OpenSSL::PKey::RSA.generate(Dpop.config.generated_key_size).to_pem
|
18
|
+
end
|
19
|
+
|
20
|
+
module_function :generate
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dpop
|
4
|
+
# Generates Proof JWT's
|
5
|
+
class ProofGenerator
|
6
|
+
JWT_TYPE = "dpop+jwt"
|
7
|
+
RSA = "RSA"
|
8
|
+
|
9
|
+
# Error for DPoP key being uninitialized
|
10
|
+
class MissingKeyError < StandardError
|
11
|
+
def initialize
|
12
|
+
super("DPoP key blank")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Error for unsupported key formats
|
17
|
+
class InvalidKeyError < StandardError
|
18
|
+
def initialize(key)
|
19
|
+
super("Unrecognized key format for DPoP key: #{key}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(dpop_key, alg)
|
24
|
+
raise MissingKeyError if dpop_key.blank?
|
25
|
+
|
26
|
+
@dpop_key = dpop_key
|
27
|
+
@alg = alg
|
28
|
+
end
|
29
|
+
|
30
|
+
# Takes the path being called. Returns a signed JWT
|
31
|
+
def create_dpop_proof(htu:, htm:, nonce: nil, ath: nil, iat: Time.now.to_i, jti: SecureRandom.uuid, **additional)
|
32
|
+
private_key, public_key = keys
|
33
|
+
|
34
|
+
headers = create_headers(public_key)
|
35
|
+
|
36
|
+
payload = create_payload(
|
37
|
+
htu: htu, htm: htm, ath: ath, iat: iat, jti: jti, nonce: nonce, **additional
|
38
|
+
)
|
39
|
+
|
40
|
+
JWT.encode(payload, private_key, @alg, headers)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def create_headers(public_key)
|
46
|
+
{
|
47
|
+
alg: @alg,
|
48
|
+
typ: JWT_TYPE,
|
49
|
+
jwk: public_key
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_payload(htu:, htm:, nonce: nil, ath: nil, iat: Time.now.to_i, jti: SecureRandom.uuid, **additional)
|
54
|
+
{
|
55
|
+
htu: htu,
|
56
|
+
htm: htm,
|
57
|
+
ath: ath,
|
58
|
+
iat: iat,
|
59
|
+
jti: jti,
|
60
|
+
nonce: nonce
|
61
|
+
}.merge(additional).compact
|
62
|
+
end
|
63
|
+
|
64
|
+
# We only support RSA keys in the form of pem strings.
|
65
|
+
def keys
|
66
|
+
return [@private_key, @public_key] if defined?(@private_key) && defined?(@public_key)
|
67
|
+
|
68
|
+
raise InvalidKeyError, @dpop_key unless @dpop_key.instance_of? String
|
69
|
+
|
70
|
+
@private_key = OpenSSL::PKey::RSA.new(@dpop_key)
|
71
|
+
@public_key = {
|
72
|
+
kty: RSA,
|
73
|
+
n: Base64.urlsafe_encode64(@private_key.n.to_s(2), padding: false),
|
74
|
+
e: Base64.urlsafe_encode64(@private_key.e.to_s(2), padding: false)
|
75
|
+
}
|
76
|
+
[@private_key, @public_key]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/dpop/railtie.rb
ADDED
data/lib/dpop/version.rb
CHANGED
data/lib/dpop.rb
CHANGED
@@ -1,8 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "active_support/json"
|
4
|
+
require "active_support/key_generator"
|
5
|
+
require "active_support/message_encryptor"
|
6
|
+
require "jwt"
|
7
|
+
require "openssl"
|
8
|
+
require "securerandom"
|
4
9
|
|
10
|
+
# DPoP top level methods
|
5
11
|
module Dpop
|
6
12
|
class Error < StandardError; end
|
7
|
-
|
13
|
+
|
14
|
+
autoload :Configuration, "dpop/configuration"
|
15
|
+
autoload :Controller, "dpop/controller"
|
16
|
+
autoload :CookieJar, "dpop/cookie_jar"
|
17
|
+
autoload :Encryptor, "dpop/encryptor"
|
18
|
+
autoload :KeyGenerator, "dpop/key_generator"
|
19
|
+
autoload :ProofGenerator, "dpop/proof_generator"
|
20
|
+
autoload :Version, "dpop/version"
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Configure Dpop application-wide settings.
|
24
|
+
#
|
25
|
+
# Yields a configuration object that can be used to override default settings.
|
26
|
+
def configure
|
27
|
+
yield(configuration) if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the client's Configuration object, or creates one if not yet created.
|
31
|
+
def configuration
|
32
|
+
@configuration ||= Dpop::Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
alias config configuration
|
36
|
+
|
37
|
+
def get_proof_with_key(dpop_key, **args)
|
38
|
+
generator = Dpop::ProofGenerator.new(dpop_key, "RS256")
|
39
|
+
generator.create_dpop_proof(args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def generate_key_pair(alg = :rsa)
|
43
|
+
Dpop::KeyGenerator.generate(alg)
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_integration
|
47
|
+
require "dpop/railtie" if defined?(Rails::Railtie)
|
48
|
+
end
|
49
|
+
end
|
8
50
|
end
|
51
|
+
|
52
|
+
Dpop.load_integration
|
metadata
CHANGED
@@ -1,15 +1,127 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dpop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WilliamNHarvey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: jwt
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: openssl
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
13
125
|
description:
|
14
126
|
email:
|
15
127
|
- williamnharvey@gmail.com
|
@@ -17,14 +129,23 @@ executables: []
|
|
17
129
|
extensions: []
|
18
130
|
extra_rdoc_files: []
|
19
131
|
files:
|
132
|
+
- ".rspec"
|
133
|
+
- ".rubocop.yml"
|
20
134
|
- CHANGELOG.md
|
21
135
|
- Gemfile
|
136
|
+
- Gemfile.lock
|
22
137
|
- LICENSE
|
23
138
|
- README.md
|
24
139
|
- Rakefile
|
25
140
|
- lib/dpop.rb
|
141
|
+
- lib/dpop/configuration.rb
|
142
|
+
- lib/dpop/controller.rb
|
143
|
+
- lib/dpop/cookie_jar.rb
|
144
|
+
- lib/dpop/encryptor.rb
|
145
|
+
- lib/dpop/key_generator.rb
|
146
|
+
- lib/dpop/proof_generator.rb
|
147
|
+
- lib/dpop/railtie.rb
|
26
148
|
- lib/dpop/version.rb
|
27
|
-
- sig/dpop.rbs
|
28
149
|
homepage: https://github.com/WilliamNHarvey/dpop-ruby
|
29
150
|
licenses: []
|
30
151
|
metadata:
|
@@ -39,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
160
|
requirements:
|
40
161
|
- - ">="
|
41
162
|
- !ruby/object:Gem::Version
|
42
|
-
version: 2.
|
163
|
+
version: 2.5.0
|
43
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
165
|
requirements:
|
45
166
|
- - ">="
|
@@ -49,6 +170,6 @@ requirements: []
|
|
49
170
|
rubygems_version: 3.1.2
|
50
171
|
signing_key:
|
51
172
|
specification_version: 4
|
52
|
-
summary: Implementation of DPoP (
|
53
|
-
Layer) for Ruby and Rails
|
173
|
+
summary: Implementation of DPoP (Demonstrating Proof-of-Possession at the Application
|
174
|
+
Layer) for Ruby and Rails
|
54
175
|
test_files: []
|
data/sig/dpop.rbs
DELETED