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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ba26d6eaed75b285ebfc99c508c1293fcfa256829ec0bfdd8aae3d53bb32614
4
- data.tar.gz: 0f4798665bd519bf0b09da6ff765fb656b4cbd634cfa14d0fe02b9277567bba7
3
+ metadata.gz: 3c45d1b462c7f11136d16d76f13b72ab45153f4e9bd6e94ed1ccd9306a0f325e
4
+ data.tar.gz: e36e70c0e2280ddd766c428580c0a3d41070e674eb63df01f96e5cc4ce670da7
5
5
  SHA512:
6
- metadata.gz: ee25cbc235574214e7a6584f6ec2adc69adf79393307b96f6e5b2e0fe884c9c73a1b5ce4b06c6772e4719eb3b070fdc1d093328a99c9b199932e929c29c14b43
7
- data.tar.gz: 11c096d289512d8650e82b4ca5e9ec2ac03344cd0d7d70ec12375405d7aac292dab3f910b276eddeb8d4f8b874a9f6cde31e5edc99eea4ebbef54464403195c3
6
+ metadata.gz: 04d6b27926a988870273e8298388c1f44579cbcfa6d5f6c95667f9e82704881e1be6b9f5cd149561d52fc21f36371148c1e513461af6c5cb00cb44e91e073358
7
+ data.tar.gz: 3af959d552bd694fc8d10cf976416be52ef8f6e833eb45b751f112d657aaeaf4ce71b0bf1aa2f4f0f2011cbdb4b7a7217b0290d3c9e328b6be6016b51c63929a
data/.simplecov ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.start do
4
+ enable_coverage :branch
5
+ primary_coverage :branch
6
+
7
+ groups.delete('Channels')
8
+ groups.delete('Libraries')
9
+
10
+ add_filter('lib/usps/jwt_auth/concern.rb')
11
+ end
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.jwt = {
28
- audience: ENV.fetch('JWT_AUDIENCE'),
29
- issuer_base: ENV.fetch('JWT_ISSUER_BASE', 'usps:1'),
30
- issuers: ENV.fetch('JWT_ISSUERS', 'admin:1').split(',')
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) }
@@ -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.configuration.keys_path)
9
- FileUtils.touch(Usps::JwtAuth.configuration.keys_path.join('.keep'))
10
- FileUtils.mkdir_p(Usps::JwtAuth.configuration.public_keys_path)
11
- FileUtils.touch(Usps::JwtAuth.configuration.public_keys_path.join('.keep'))
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.configuration.keys_path}
17
- /!#{Usps::JwtAuth.configuration.keys_path.join('.keep')}
18
- /#{Usps::JwtAuth.configuration.public_keys_path}
19
- /!#{Usps::JwtAuth.configuration.public_keys_path.join('.keep')}
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.configuration.environment.test? && fetch_jwt.nil?
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.configuration.is_admin.call(@current_user) && session.key?('impersonate')
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.configuration.find_member.call(session['impersonate']['impersonated'])
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.configuration.find_member.call(jwt['certificate']) if fetch_jwt
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.configuration.environment.production? ? '.aws.usps.org' : 'localhost'
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.configuration.jwt.audience],
99
- issuer: JwtAuth.configuration.jwt.issuer
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.configuration.jwt.audience}"
107
- url = JwtAuth.configuration.environment.development? ? local : production
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.configuration.environment.test?
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.configuration.jwt.audience], issuer: JwtAuth.configuration.jwt.issuers.first
119
+ audience: [JwtAuth.config.jwt.audience], issuer: JwtAuth.config.jwt.issuers.first
120
120
  )
121
121
  )
122
122
  end
@@ -5,16 +5,22 @@ module Usps
5
5
  # Configure JWT Authentication
6
6
  #
7
7
  class Config
8
- attr_accessor :environment, :is_admin, :find_member
9
- attr_writer :key_size
10
- attr_reader :keys_path, :public_keys_path, :jwt
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
- yield self if block_given?
14
- end
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
- def key_size
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 jwt=(hash)
29
- @jwt = ActiveSupport::InheritableOptions.new(
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 issuer
39
- /\A#{jwt.issuer_base}(?::#{RegExp.union(jwt.issuers)})?\z/
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
@@ -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.issuer_pattern(issuer)
20
- /\A#{JwtAuth.configuration.jwt.issuer_base}(?:\z|:#{issuer})/
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
- CONFIG.merge(
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.configuration.public_keys_path}/#{fingerprint(token)}.pub"))
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)
@@ -23,7 +23,7 @@ module Usps
23
23
  end
24
24
 
25
25
  def encode(data)
26
- JWT.encode(payload(data), private_key, JwtAuth::ALGORITHM)
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.configuration.jwt.issuer_base, @issuer].join(':'),
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.configuration.keys_path}/*"]
58
+ Dir["#{JwtAuth.config.keys_path}/*"]
59
59
  end
60
60
 
61
61
  def store_private_key
62
- path = "#{JwtAuth.configuration.keys_path}/#{fingerprint}"
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.configuration.public_keys_path}/#{fingerprint}.pub"
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.configuration.key_size)
74
+ OpenSSL::PKey::RSA.generate(JwtAuth.config.key_size)
75
75
  end
76
76
  end
77
77
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Usps
4
4
  module JwtAuth
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.1-1'
6
6
  end
7
7
  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