keratin-authn 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c8460a42cb6b34ecd0115366206cbda19e4e474
4
- data.tar.gz: 26ccf34ff11d3da767e8e78fea9df8f883f92442
3
+ metadata.gz: 675c925f05dfabe4bd0f21df787374d84656bb54
4
+ data.tar.gz: 6707f4d2b665485192152bf7a59c5a1d62800e7d
5
5
  SHA512:
6
- metadata.gz: b1dc1e52c700b90568b3c9c6548f703c7f1c817d9667a2ebb6fea983169de7f5c2ddff3b329e07999044b426c9023e61ebbd7f5036179a28829ed6bfc49a1000
7
- data.tar.gz: 3efd27f4e4a586f9f978fb735ac7ab5a8e8b7277bd8ac9ad6d55e6125c2ac3f8f5e35b35ff018c9727fffe6e0f780e0310feb35b4bd2228363125e47ac982a98
6
+ metadata.gz: 9c539082ff017a26237d848342769115ca0ad978ec470505e516fcb91879499fc7bb27d0f43601a434b480e70acfbb55ec2c124686e6b8bc3319e2d38a197d13
7
+ data.tar.gz: d41c0567fd3f85b164c82a8c0bb5237d4f0e0e9865fb3a0bfe6d2a5e6996381b55cb1c3658713be50525cdafce31480e34d1d73a4d0523a1530104f207ad8129
data/README.md CHANGED
@@ -23,12 +23,18 @@ Keratin::AuthN.config.tap do |config|
23
23
  # The base URL of your Keratin AuthN service
24
24
  config.issuer = 'https://authn.myapp.com'
25
25
 
26
- # The domain of your application
26
+ # The domain of your application (no protocol)
27
27
  config.audience = 'myapp.com'
28
28
 
29
29
  # HTTP basic auth for using AuthN's private endpoints
30
30
  config.username = 'secret'
31
31
  config.password = 'secret'
32
+
33
+ # OPTIONAL: enables debugging for the JWT verification process
34
+ config.logger = Rails.logger
35
+
36
+ # OPTIONAL: allows private API calls to use private network routing
37
+ config.authn_url = 'https://authn.internal.dns/
32
38
  end
33
39
  ```
34
40
 
@@ -137,9 +143,9 @@ AuthN provides helpers for working with tokens in your application's controller
137
143
  In your `test/test_helper.rb` or equivalent:
138
144
 
139
145
  ```ruby
140
- # Configuring AuthN to use the MockSignatureVerifier will stop your tests from attempting to connect
141
- # to the remote issuer during tests.
142
- Keratin::AuthN.signature_verifier = Keratin::AuthN::MockSignatureVerifier.new
146
+ # Configuring AuthN to use the MockKeychain will stop your tests from attempting to connect to the
147
+ # remote issuer during tests.
148
+ Keratin::AuthN.signature_verifier = Keratin::AuthN::MockKeychain.new
143
149
 
144
150
  # Including the Test::Helpers module grants access to `id_token_for(user.account_id)`, so that you
145
151
  # can test your system with real tokens.
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ['lib']
31
31
 
32
- spec.add_dependency 'json-jwt'
32
+ spec.add_dependency 'json-jwt', '!= 1.8.0'
33
33
  spec.add_dependency 'lru_redux'
34
34
 
35
35
  spec.add_development_dependency 'bundler', '~> 1.13'
@@ -1,17 +1,17 @@
1
1
  require_relative 'authn/version'
2
2
  require_relative 'authn/engine' if defined?(Rails)
3
3
  require_relative 'authn/id_token_verifier'
4
- require_relative 'authn/remote_signature_verifier'
5
- require_relative 'authn/mock_signature_verifier'
6
- require_relative 'authn/issuer'
4
+ require_relative 'authn/fetching_keychain'
5
+ require_relative 'authn/mock_keychain'
6
+ require_relative 'authn/api'
7
7
 
8
8
  require 'lru_redux'
9
9
  require 'json/jwt'
10
10
 
11
11
  module Keratin
12
12
  def self.authn
13
- @authn ||= AuthN::Issuer.new(
14
- AuthN.config.issuer,
13
+ @authn ||= AuthN::API.new(
14
+ AuthN.config.authn_url || AuthN.config.issuer,
15
15
  username: AuthN.config.username,
16
16
  password: AuthN.config.password
17
17
  )
@@ -19,7 +19,7 @@ module Keratin
19
19
 
20
20
  module AuthN
21
21
  class Config
22
- # the domain (host) of the main application.
22
+ # the domain (host) of the main application. no protocol.
23
23
  # e.g. "audience.tech"
24
24
  attr_accessor :audience
25
25
 
@@ -27,6 +27,11 @@ module Keratin
27
27
  # e.g. "https://issuer.tech"
28
28
  attr_accessor :issuer
29
29
 
30
+ # the base url for API calls. this is useful if you've divided your network so private API
31
+ # requests can not be probed by public devices. it is optional, and will default to issuer.
32
+ # e.g. "https://authn.internal.dns"
33
+ attr_accessor :authn_url
34
+
30
35
  # how long (in seconds) to keep keys in the keychain before refreshing.
31
36
  # default: 3600
32
37
  attr_accessor :keychain_ttl
@@ -51,38 +56,29 @@ module Keratin
51
56
  config.logger.debug{ yield } if config.logger
52
57
  end
53
58
 
54
- # The default strategy for signature verification will find the JWT's issuer, fetch the JWKs
55
- # from that server, choose the correct key by id, and finally verify the JWT. The keys are
56
- # then cached in memory to reduce network traffic.
57
- def self.signature_verifier
58
- @verifier ||= RemoteSignatureVerifier.new(
59
- LruRedux::TTL::ThreadSafeCache.new(25, config.keychain_ttl)
60
- )
59
+ # The default keychain will fetch JWKs from the configured issuer and return the correct key by
60
+ # id. Keys are cached in memory to reduce network traffic.
61
+ def self.keychain
62
+ @keychain ||= FetchingKeychain.new(issuer: config.issuer, ttl: config.keychain_ttl)
61
63
  end
62
64
 
63
- # If the default strategy is not desired (as in host application tests), different strategies
64
- # may be specified here. The strategy must define a `verify(jwt)` method.
65
- def self.signature_verifier=(val)
66
- unless val.respond_to?(:verify) && val.method(:verify).arity == 1
67
- raise ArgumentError, 'Please ensure that your signature verifier has been instantiated and implements `def verify(jwt)`.'
65
+ # If the default keychain is not desired (as in host application tests), different keychain may
66
+ # be specified here. The keychain must define a `[](kid)` method.
67
+ def self.keychain=(val)
68
+ unless val.respond_to?(:[]) && val.method(:[]).arity == 1
69
+ raise ArgumentError, 'Please ensure that your keychain has been instantiated and implements `[](kid)`.'
68
70
  end
69
71
 
70
- @verifier = val
72
+ @keychain = val
71
73
  end
72
74
 
73
75
  class << self
74
76
  # safely fetches a subject from the id token after checking relevant claims and
75
77
  # verifying the signature.
76
78
  def subject_from(id_token, audience: Keratin::AuthN.config.audience)
77
- verifier = IDTokenVerifier.new(id_token, signature_verifier, audience)
79
+ verifier = IDTokenVerifier.new(id_token, keychain, audience)
78
80
  verifier.subject if verifier.verified?
79
81
  end
80
-
81
- def logout_url(return_to: nil)
82
- query = {redirect_uri: return_to}.to_param if return_to
83
-
84
- "#{config.issuer}/sessions/logout#{'?' if query}#{query}"
85
- end
86
82
  end
87
83
  end
88
84
  end
@@ -2,7 +2,11 @@ require 'keratin/client'
2
2
  require 'net/http'
3
3
 
4
4
  module Keratin::AuthN
5
- class Issuer < Keratin::Client
5
+ class API < Keratin::Client
6
+ def get(account_id)
7
+ super(path: "/accounts/#{account_id}")
8
+ end
9
+
6
10
  def update(account_id, username:)
7
11
  patch(path: "/accounts/#{account_id}", body: {
8
12
  username: username
@@ -33,21 +37,5 @@ module Keratin::AuthN
33
37
  def expire_password(account_id)
34
38
  patch(path: "/accounts/#{account_id}/expire_password")
35
39
  end
36
-
37
- def signing_key(kid)
38
- keys.find{|k| k['use'] == 'sig' && (kid.blank? || kid == k['kid']) }
39
- end
40
-
41
- private def configuration
42
- @configuration ||= get(path: '/configuration').data
43
- end
44
-
45
- private def keys
46
- JSON::JWK::Set.new(
47
- JSON.parse(
48
- Net::HTTP.get(URI.parse(configuration['jwks_uri']))
49
- )
50
- )
51
- end
52
40
  end
53
41
  end
@@ -0,0 +1,25 @@
1
+ module Keratin::AuthN
2
+ class FetchingKeychain
3
+ def initialize(issuer:, ttl:)
4
+ @cache = LruRedux::TTL::ThreadSafeCache.new(25, ttl)
5
+ @issuer = issuer.chomp('/')
6
+ end
7
+
8
+ def [](kid)
9
+ @cache.getset(kid){ fetch(kid) }
10
+ end
11
+
12
+ def clear
13
+ @cache.clear
14
+ end
15
+
16
+ private def fetch(kid)
17
+ keys = JSON::JWK::Set.new(
18
+ JSON.parse(
19
+ Net::HTTP.get(URI.parse("#{@issuer}/jwks"))
20
+ )
21
+ )
22
+ keys.find{|k| k['use'] == 'sig' && (kid.blank? || kid == k['kid']) }
23
+ end
24
+ end
25
+ end
@@ -2,9 +2,9 @@ require 'uri'
2
2
 
3
3
  module Keratin::AuthN
4
4
  class IDTokenVerifier
5
- def initialize(str, signature_verifier, audience)
5
+ def initialize(str, keychain, audience)
6
6
  @id_token = str
7
- @signature_verifier = signature_verifier
7
+ @keychain = keychain
8
8
  @audience = audience
9
9
  @time = Time.now.to_i
10
10
  end
@@ -51,7 +51,9 @@ module Keratin::AuthN
51
51
  end
52
52
 
53
53
  def token_intact?
54
- @signature_verifier.verify(jwt)
54
+ jwt.verify!(@keychain[jwt.kid])
55
+ rescue JSON::JWT::VerificationFailed, JSON::JWT::UnexpectedAlgorithm
56
+ false
55
57
  end
56
58
 
57
59
  private def jwt
@@ -1,6 +1,6 @@
1
1
  module Keratin::AuthN
2
- class MockSignatureVerifier
3
- def verify(_)
2
+ class MockKeychain
3
+ def [](kid)
4
4
  true
5
5
  end
6
6
  end
@@ -1,5 +1,5 @@
1
1
  module Keratin # rubocop:disable Style/ClassAndModuleChildren
2
2
  module AuthN
3
- VERSION = '0.6.1'
3
+ VERSION = '1.0.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keratin-authn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lance Ivy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-26 00:00:00.000000000 Z
11
+ date: 2017-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json-jwt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "!="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.8.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "!="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 1.8.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: lru_redux
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -155,11 +155,11 @@ files:
155
155
  - bin/setup
156
156
  - keratin-authn.gemspec
157
157
  - lib/keratin/authn.rb
158
+ - lib/keratin/authn/api.rb
158
159
  - lib/keratin/authn/engine.rb
160
+ - lib/keratin/authn/fetching_keychain.rb
159
161
  - lib/keratin/authn/id_token_verifier.rb
160
- - lib/keratin/authn/issuer.rb
161
- - lib/keratin/authn/mock_signature_verifier.rb
162
- - lib/keratin/authn/remote_signature_verifier.rb
162
+ - lib/keratin/authn/mock_keychain.rb
163
163
  - lib/keratin/authn/test/helpers.rb
164
164
  - lib/keratin/authn/testing.rb
165
165
  - lib/keratin/authn/version.rb
@@ -1,19 +0,0 @@
1
- module Keratin::AuthN
2
- class RemoteSignatureVerifier
3
- attr_reader :keychain
4
-
5
- def initialize(keychain)
6
- @keychain = keychain
7
- end
8
-
9
- def verify(jwt)
10
- jwt.verify!(key(jwt['iss'], jwt.kid))
11
- rescue JSON::JWT::VerificationFailed, JSON::JWT::UnexpectedAlgorithm
12
- false
13
- end
14
-
15
- private def key(issuer, kid)
16
- keychain.getset(kid){ Issuer.new(issuer).signing_key(kid) }
17
- end
18
- end
19
- end