usps-jwt_auth 0.1.1 → 0.2.1.pre.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/.simplecov +11 -0
- data/README.md +8 -8
- data/lib/tasks/jwt_auth.rake +8 -8
- data/lib/usps/jwt_auth/concern.rb +11 -11
- data/lib/usps/jwt_auth/config.rb +20 -17
- data/lib/usps/jwt_auth/decode.rb +11 -14
- data/lib/usps/jwt_auth/encode.rb +6 -6
- data/lib/usps/jwt_auth/version.rb +1 -1
- data/lib/usps/jwt_auth.rb +4 -3
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c45d1b462c7f11136d16d76f13b72ab45153f4e9bd6e94ed1ccd9306a0f325e
|
|
4
|
+
data.tar.gz: e36e70c0e2280ddd766c428580c0a3d41070e674eb63df01f96e5cc4ce670da7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04d6b27926a988870273e8298388c1f44579cbcfa6d5f6c95667f9e82704881e1be6b9f5cd149561d52fc21f36371148c1e513461af6c5cb00cb44e91e073358
|
|
7
|
+
data.tar.gz: 3af959d552bd694fc8d10cf976416be52ef8f6e833eb45b751f112d657aaeaf4ce71b0bf1aa2f4f0f2011cbdb4b7a7217b0290d3c9e328b6be6016b51c63929a
|
data/.simplecov
ADDED
data/README.md
CHANGED
|
@@ -21,14 +21,14 @@ bundle exec rails usps:jwt:install
|
|
|
21
21
|
```ruby
|
|
22
22
|
Usps::JwtAuth.configure do |config|
|
|
23
23
|
config.environment = Rails.env
|
|
24
|
-
config.keys_path = 'config/keys'
|
|
25
|
-
config.public_keys_path = 'config/public_keys'
|
|
26
|
-
|
|
27
|
-
config.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
# config.keys_path = 'config/keys'
|
|
25
|
+
# config.public_keys_path = 'config/public_keys'
|
|
26
|
+
# config.key_size = 4096
|
|
27
|
+
# config.algorithm = 'RS512'
|
|
28
|
+
|
|
29
|
+
config.audience = ENV.fetch('JWT_AUDIENCE')
|
|
30
|
+
# config.issuer_base = ENV.fetch('JWT_ISSUER_BASE', 'usps:1')
|
|
31
|
+
config.issuers = ENV.fetch('JWT_ISSUERS', 'admin:1').split(',')
|
|
32
32
|
|
|
33
33
|
config.is_admin = ->(user) { Pundit.policy(user, :admin).admin? }
|
|
34
34
|
config.find_member = ->(certificate) { Members::Member.find(certificate) }
|
data/lib/tasks/jwt_auth.rake
CHANGED
|
@@ -5,18 +5,18 @@ namespace :usps do
|
|
|
5
5
|
desc 'Setup JWT Authentication'
|
|
6
6
|
task install: :environment do
|
|
7
7
|
# Ensure keys directories exist
|
|
8
|
-
FileUtils.mkdir_p(Usps::JwtAuth.
|
|
9
|
-
FileUtils.touch(Usps::JwtAuth.
|
|
10
|
-
FileUtils.mkdir_p(Usps::JwtAuth.
|
|
11
|
-
FileUtils.touch(Usps::JwtAuth.
|
|
8
|
+
FileUtils.mkdir_p(Usps::JwtAuth.config.keys_path)
|
|
9
|
+
FileUtils.touch(Usps::JwtAuth.config.keys_path.join('.keep'))
|
|
10
|
+
FileUtils.mkdir_p(Usps::JwtAuth.config.public_keys_path)
|
|
11
|
+
FileUtils.touch(Usps::JwtAuth.config.public_keys_path.join('.keep'))
|
|
12
12
|
|
|
13
13
|
# Ignore keys directories from git
|
|
14
14
|
File.open('.gitignore', 'a') do |file|
|
|
15
15
|
file.puts <<~IGNORE
|
|
16
|
-
/#{Usps::JwtAuth.
|
|
17
|
-
/!#{Usps::JwtAuth.
|
|
18
|
-
/#{Usps::JwtAuth.
|
|
19
|
-
/!#{Usps::JwtAuth.
|
|
16
|
+
/#{Usps::JwtAuth.config.keys_path}
|
|
17
|
+
/!#{Usps::JwtAuth.config.keys_path.join('.keep')}
|
|
18
|
+
/#{Usps::JwtAuth.config.public_keys_path}
|
|
19
|
+
/!#{Usps::JwtAuth.config.public_keys_path.join('.keep')}
|
|
20
20
|
IGNORE
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -20,7 +20,7 @@ module Usps
|
|
|
20
20
|
def current_user
|
|
21
21
|
return @current_user if defined?(@current_user)
|
|
22
22
|
|
|
23
|
-
stub_jwt! if JwtAuth.
|
|
23
|
+
stub_jwt! if JwtAuth.config.environment.test? && fetch_jwt.nil?
|
|
24
24
|
|
|
25
25
|
current_user_from_jwt
|
|
26
26
|
end
|
|
@@ -49,10 +49,10 @@ module Usps
|
|
|
49
49
|
|
|
50
50
|
def load_current_user
|
|
51
51
|
@current_user = jwt_user
|
|
52
|
-
return @current_user unless JwtAuth.
|
|
52
|
+
return @current_user unless JwtAuth.config.is_admin.call(@current_user) && session.key?('impersonate')
|
|
53
53
|
|
|
54
54
|
# Admin has entered impersonation mode -- override current_user
|
|
55
|
-
@current_user = JwtAuth.
|
|
55
|
+
@current_user = JwtAuth.config.find_member.call(session['impersonate']['impersonated'])
|
|
56
56
|
rescue JWT::ExpiredSignature
|
|
57
57
|
clear_jwt
|
|
58
58
|
nil
|
|
@@ -63,7 +63,7 @@ module Usps
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def jwt_user
|
|
66
|
-
@jwt_user ||= JwtAuth.
|
|
66
|
+
@jwt_user ||= JwtAuth.config.find_member.call(jwt['certificate']) if fetch_jwt
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def set_new_jwt
|
|
@@ -80,7 +80,7 @@ module Usps
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def cookie_domain
|
|
83
|
-
JwtAuth.
|
|
83
|
+
JwtAuth.config.environment.production? ? '.aws.usps.org' : 'localhost'
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def clear_jwt
|
|
@@ -95,28 +95,28 @@ module Usps
|
|
|
95
95
|
def jwt
|
|
96
96
|
Decode.decode(
|
|
97
97
|
fetch_jwt,
|
|
98
|
-
audience: [JwtAuth.
|
|
99
|
-
issuer: JwtAuth.
|
|
98
|
+
audience: [JwtAuth.config.jwt.audience],
|
|
99
|
+
issuer: JwtAuth.config.jwt.issuer
|
|
100
100
|
)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def redirect_to_login
|
|
104
104
|
url = 'https://www.usps.org/jwt'
|
|
105
105
|
local = "#{url}?local&port=#{ENV.fetch('PORT', '3000')}"
|
|
106
|
-
production = "#{url}?application=#{JwtAuth.
|
|
107
|
-
url = JwtAuth.
|
|
106
|
+
production = "#{url}?application=#{JwtAuth.config.jwt.audience}"
|
|
107
|
+
url = JwtAuth.config.environment.development? ? local : production
|
|
108
108
|
url = "#{url}&path=#{request.path}"
|
|
109
109
|
|
|
110
110
|
redirect_to(url, allow_other_host: true)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def stub_jwt!
|
|
114
|
-
raise 'Cannot stub JWT outside of test environment!' unless JwtAuth.
|
|
114
|
+
raise 'Cannot stub JWT outside of test environment!' unless JwtAuth.config.environment.test?
|
|
115
115
|
|
|
116
116
|
store_jwt(
|
|
117
117
|
Encode.encode(
|
|
118
118
|
{ certificate: ENV['STUB_CERTIFICATE'].presence || 'E123456' },
|
|
119
|
-
audience: [JwtAuth.
|
|
119
|
+
audience: [JwtAuth.config.jwt.audience], issuer: JwtAuth.config.jwt.issuers.first
|
|
120
120
|
)
|
|
121
121
|
)
|
|
122
122
|
end
|
data/lib/usps/jwt_auth/config.rb
CHANGED
|
@@ -5,16 +5,22 @@ module Usps
|
|
|
5
5
|
# Configure JWT Authentication
|
|
6
6
|
#
|
|
7
7
|
class Config
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
REQUIRED_OPTIONS = %i[audience is_admin find_member].freeze
|
|
9
|
+
|
|
10
|
+
attr_accessor :environment, :key_size, :algorithm, :issuer_base, :issuers, :audience, :is_admin, :find_member
|
|
11
|
+
attr_reader :keys_path, :public_keys_path
|
|
11
12
|
|
|
12
13
|
def initialize
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
@environment = ActiveSupport::StringInquirer.new('development')
|
|
15
|
+
@keys_path = Pathname.new('config/keys')
|
|
16
|
+
@public_keys_path = Pathname.new('config/public_keys')
|
|
17
|
+
@key_size = 4096
|
|
18
|
+
@algorithm = 'RS512'
|
|
19
|
+
@issuer_base = 'usps:1'
|
|
20
|
+
@issuers = []
|
|
21
|
+
@audience = nil
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
@key_size || 4096
|
|
23
|
+
yield self if block_given? # Also support setting options on initialize
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
def keys_path=(path)
|
|
@@ -25,18 +31,15 @@ module Usps
|
|
|
25
31
|
@public_keys_path = path.is_a?(Pathname) ? path : Pathname.new(path)
|
|
26
32
|
end
|
|
27
33
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
issuer_base: 'usps:1',
|
|
32
|
-
issuers: [],
|
|
33
|
-
audience: nil
|
|
34
|
-
}.merge(hash)
|
|
35
|
-
)
|
|
34
|
+
def issuer
|
|
35
|
+
/\A#{issuer_base}(?::#{Regexp.union(issuers)})?\z/
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def
|
|
39
|
-
|
|
38
|
+
def validate!
|
|
39
|
+
missing_required_options = REQUIRED_OPTIONS.select { public_send(it).nil? }
|
|
40
|
+
return unless missing_required_options.any?
|
|
41
|
+
|
|
42
|
+
raise "Missing required options: #{missing_required_options.join(', ')}"
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
end
|
data/lib/usps/jwt_auth/decode.rb
CHANGED
|
@@ -5,19 +5,19 @@ module Usps
|
|
|
5
5
|
# Decode and validate data from a JWT
|
|
6
6
|
#
|
|
7
7
|
class Decode
|
|
8
|
-
CONFIG = {
|
|
9
|
-
required_claims: %w[iss exp],
|
|
10
|
-
verify_iss: true,
|
|
11
|
-
verify_aud: true,
|
|
12
|
-
algorithm: JwtAuth::ALGORITHM
|
|
13
|
-
}.freeze
|
|
14
|
-
|
|
15
8
|
def self.decode(token, audience: [], issuer: nil)
|
|
16
9
|
new.decode(token, audience: audience, issuer: issuer)
|
|
17
10
|
end
|
|
18
11
|
|
|
19
|
-
def self.
|
|
20
|
-
|
|
12
|
+
def self.token_options(audience: [], issuer: nil)
|
|
13
|
+
{
|
|
14
|
+
required_claims: %w[iss exp],
|
|
15
|
+
verify_iss: true,
|
|
16
|
+
verify_aud: true,
|
|
17
|
+
algorithm: JwtAuth.config.algorithm,
|
|
18
|
+
aud: audience,
|
|
19
|
+
iss: /\A#{JwtAuth.config.issuer_base}(?:\z|:#{issuer})/
|
|
20
|
+
}
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def decode(token, audience: [], issuer: nil)
|
|
@@ -25,10 +25,7 @@ module Usps
|
|
|
25
25
|
token,
|
|
26
26
|
public_key(token),
|
|
27
27
|
true,
|
|
28
|
-
|
|
29
|
-
aud: audience,
|
|
30
|
-
iss: self.class.issuer_pattern(issuer)
|
|
31
|
-
)
|
|
28
|
+
self.class.token_options(audience:, issuer:)
|
|
32
29
|
)
|
|
33
30
|
result[0]['data']
|
|
34
31
|
end
|
|
@@ -36,7 +33,7 @@ module Usps
|
|
|
36
33
|
private
|
|
37
34
|
|
|
38
35
|
def public_key(token)
|
|
39
|
-
OpenSSL::PKey::RSA.new(File.read("#{JwtAuth.
|
|
36
|
+
OpenSSL::PKey::RSA.new(File.read("#{JwtAuth.config.public_keys_path}/#{fingerprint(token)}.pub"))
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
def fingerprint(token)
|
data/lib/usps/jwt_auth/encode.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Usps
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def encode(data)
|
|
26
|
-
JWT.encode(payload(data), private_key, JwtAuth
|
|
26
|
+
JWT.encode(payload(data), private_key, JwtAuth.config.algorithm)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def public_key
|
|
@@ -44,7 +44,7 @@ module Usps
|
|
|
44
44
|
{
|
|
45
45
|
data: data,
|
|
46
46
|
exp: expires_at.to_i,
|
|
47
|
-
iss: [JwtAuth.
|
|
47
|
+
iss: [JwtAuth.config.issuer_base, @issuer].join(':'),
|
|
48
48
|
aud: @audience,
|
|
49
49
|
key: fingerprint
|
|
50
50
|
}
|
|
@@ -55,23 +55,23 @@ module Usps
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def keys
|
|
58
|
-
Dir["#{JwtAuth.
|
|
58
|
+
Dir["#{JwtAuth.config.keys_path}/*"]
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def store_private_key
|
|
62
|
-
path = "#{JwtAuth.
|
|
62
|
+
path = "#{JwtAuth.config.keys_path}/#{fingerprint}"
|
|
63
63
|
File.open(path, 'w+') { |f| f.puts(private_key) }
|
|
64
64
|
::FileUtils.chmod(0o600, path)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def store_public_key
|
|
68
|
-
path = "#{JwtAuth.
|
|
68
|
+
path = "#{JwtAuth.config.public_keys_path}/#{fingerprint}.pub"
|
|
69
69
|
File.open(path, 'w+') { |f| f.puts(public_key) }
|
|
70
70
|
::FileUtils.chmod(0o644, path)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def generate_private_key
|
|
74
|
-
OpenSSL::PKey::RSA.generate(JwtAuth.
|
|
74
|
+
OpenSSL::PKey::RSA.generate(JwtAuth.config.key_size)
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
end
|
data/lib/usps/jwt_auth.rb
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'jwt'
|
|
4
|
-
require 'active_support/ordered_options'
|
|
5
4
|
require 'fileutils'
|
|
6
5
|
|
|
7
6
|
module Usps
|
|
8
7
|
# Unified configuration for handling JWT Authentication
|
|
9
8
|
#
|
|
10
9
|
module JwtAuth
|
|
11
|
-
ALGORITHM = 'RS512'
|
|
12
|
-
|
|
13
10
|
class << self
|
|
14
11
|
def configuration
|
|
15
12
|
@configuration ||= Config.new
|
|
16
13
|
end
|
|
14
|
+
alias config configuration
|
|
17
15
|
|
|
18
16
|
def configure
|
|
19
17
|
yield(configuration) if block_given?
|
|
18
|
+
configuration.validate!
|
|
20
19
|
configuration
|
|
21
20
|
end
|
|
22
21
|
|
|
@@ -38,4 +37,6 @@ require_relative 'jwt_auth/encode'
|
|
|
38
37
|
require_relative 'jwt_auth/decode'
|
|
39
38
|
require_relative 'jwt_auth/concern'
|
|
40
39
|
|
|
40
|
+
# :nocov:
|
|
41
41
|
require 'usps/jwt_auth/railtie' if defined?(Rails)
|
|
42
|
+
# :nocov:
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: usps-jwt_auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.1
|
|
4
|
+
version: 0.2.1.pre.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Julian Fiander
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activesupport
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: fileutils
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -45,6 +59,7 @@ extensions: []
|
|
|
45
59
|
extra_rdoc_files: []
|
|
46
60
|
files:
|
|
47
61
|
- ".ruby-version"
|
|
62
|
+
- ".simplecov"
|
|
48
63
|
- README.md
|
|
49
64
|
- Rakefile
|
|
50
65
|
- lib/tasks/jwt_auth.rake
|