authrocket 3.0.0 → 3.1.0

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: 138d4247bae61aa6ac0c171621a438d8b6d47db7de2a2cc9b7ad61ea7df0784d
4
- data.tar.gz: c83fe483dbdaf4d0e0152972c42e76b17ec5133114cae0b1081c082b74883cee
3
+ metadata.gz: 82ed5a2ea3b1c412a72c84aeac220c4b54e26ca5a0f9cc7248ecbca9e3c211bb
4
+ data.tar.gz: 5acc9665f07016d75af2f26d08527206749cc7e4bfe56df6df248e50a3d9fe5f
5
5
  SHA512:
6
- metadata.gz: 89f83a485b4c382eba642e16d9617bd0107833ee79abe6379fb3126a939f3515becda60182fc886a7976e739f1b45d6a60918ad82dc91cd6960fb8cebb306063
7
- data.tar.gz: d1b32f8ac525755fa7cfed8e06846c6aa6d58e124ba775ecde423901fcc7157db09f65553e27b670f2ab7ec596621d2c033ecbaab2e7da104369955cea2895a2
6
+ metadata.gz: 375bcc4653c32f973da17893ff5250131c655d7122a4ba88a3d818fc454ebfdc7c9561db996c67bb6dec141823dceb1975124b8a0714eae448ac24965ae7d2e0
7
+ data.tar.gz: b12e98876190ded64592edc86c2fd160dd8b72f75086617f72164b3f7aef90f0dc2bb6fc62195eb8e2a0b375bda7f93a1a517fdeff421fbf2b8c0d0912bd47b3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ #### 3.1.0
2
+
3
+ - Automatically handle login tokens in an Authorization header
4
+ eg: Authorization: Bearer {the-token}
5
+ - Optimize LR JWKS support to match by kid
6
+
1
7
  #### 3.0.0
2
8
 
3
9
  - NOTE: This version includes breaking changes.
data/README.md CHANGED
@@ -107,7 +107,7 @@ Sets an application-wide default realm ID. If you're using a single realm, this
107
107
  `AUTHROCKET_URL = https://api-e2.authrocket.com/v2`
108
108
  The URL of the AuthRocket API server. This may vary depending on which cluster your service is provisioned on.
109
109
 
110
- `LOGINROCKET_URL = https://sample.e2.loginrocket.com/`
110
+ `LOGINROCKET_URL = https://SAMPLE.e2.loginrocket.com/`
111
111
  The LoginRocket URL for your Connected App. Used by the streamlined Rails integration (for redirects) and for auto-retrieval of RS256 JWT keys (if AUTHROCKET_JWT_KEY is not set). If your app uses multiple realms, you'll need to handle this on your own. If you're using a custom domain, this will be that domain and will not contain 'loginrocket.com'.
112
112
 
113
113
 
@@ -1,3 +1,3 @@
1
1
  module AuthRocket
2
- VERSION = '3.0.0'
2
+ VERSION = '3.1.0'
3
3
  end
@@ -10,6 +10,14 @@ module AuthRocket::ControllerHelper
10
10
  end
11
11
  end
12
12
 
13
+ def process_authorization_header
14
+ if request.headers['authorization'] =~ %r{Bearer (.+)$}i
15
+ if s = AuthRocket::Session.from_token($1)
16
+ @_current_session = s
17
+ end
18
+ end
19
+ end
20
+
13
21
  def require_login
14
22
  unless current_session
15
23
  redirect_to ar_login_url(redirect_uri: safe_this_uri)
@@ -9,6 +9,7 @@ module AuthRocket
9
9
  include AuthRocket::ControllerHelper
10
10
  helper AuthRocket::ControllerHelper
11
11
  before_action :process_inbound_token
12
+ before_action :process_authorization_header
12
13
  end
13
14
  end
14
15
  end
@@ -19,21 +19,21 @@ module AuthRocket
19
19
  # options - :algo - one of HS256, RS256 (default: auto-detect based on :jwt_key)
20
20
  # - :within - (in seconds) Maximum time since the token was (re)issued
21
21
  # - credentials: {jwt_key: StringOrKey} - used to verify the token
22
+ # returns Session or nil
22
23
  def self.from_token(token, options={})
23
- secret = options.dig(:credentials, :jwt_key) || credentials[:jwt_key]
24
24
  if lr_url = options.dig(:credentials, :loginrocket_url) || credentials[:loginrocket_url]
25
25
  lr_url = lr_url.dup
26
26
  lr_url.concat '/' unless lr_url.ends_with?('/')
27
27
  lr_url.concat 'connect/jwks'
28
28
  end
29
-
30
- algo = options[:algo]
29
+ secret = options.dig(:credentials, :jwt_key) || credentials[:jwt_key]
31
30
  if secret.is_a?(String) && secret.length > 256
32
31
  unless secret.starts_with?('-----BEGIN ')
33
32
  secret = "-----BEGIN PUBLIC KEY-----\n#{secret}\n-----END PUBLIC KEY-----"
34
33
  end
35
34
  secret = OpenSSL::PKey.read secret
36
35
  end
36
+ algo = options[:algo]
37
37
  algo ||= 'RS256' if secret.is_a?(OpenSSL::PKey::RSA)
38
38
  algo ||= 'HS256' if secret
39
39
 
@@ -42,34 +42,22 @@ module AuthRocket
42
42
  raise Error, "Missing jwt_key; set LOGINROCKET_URL, AUTHROCKET_JWT_KEY, or pass in credentials: {loginrocket_url: ...} or {jwt_key: ...}" if secret.blank? && !jwks_eligible
43
43
  return if token.blank?
44
44
 
45
+ base_params = {token: token, within: options[:within], local_creds: options[:credentials]}
45
46
  if jwks_eligible
46
- base_params = {token: token, algo: 'RS256', within: options[:within], local_creds: options[:credentials]}
47
- load_jwk_set(lr_url, use_cached: true).each do |secret|
48
- begin
49
- return parse_jwt secret: secret, **base_params
50
- rescue JWT::DecodeError
51
- end
52
- end
53
- load_jwk_set(lr_url, use_cached: false).each do |secret|
54
- begin
55
- return parse_jwt secret: secret, **base_params
56
- rescue JWT::DecodeError
57
- end
47
+ kid = JSON.parse(JWT::Base64.url_decode(token.split('.')[0]))['kid'] rescue nil
48
+ return if kid.blank?
49
+
50
+ load_jwk_set(lr_url) unless @_jwks[kid]
51
+ if key_set = @_jwks[kid]
52
+ parse_jwt **key_set, **base_params
58
53
  end
59
- nil
60
54
  else
61
- begin
62
- parse_jwt token: token, secret: secret, algo: algo, within: options[:within], local_creds: options[:credentials]
63
- rescue JWT::DecodeError
64
- nil
65
- end
55
+ parse_jwt secret: secret, algo: algo, **base_params
66
56
  end
67
57
  end
68
58
 
69
59
  # private
70
- # raises an exception if eligible for retry using different token
71
- # returns Session on success
72
- # returns nil on a definitive token-parsed-but-invalid
60
+ # returns Session or nil
73
61
  def self.parse_jwt(token:, secret:, algo:, within:, local_creds: nil)
74
62
  opts = {
75
63
  algorithm: algo,
@@ -124,32 +112,16 @@ module AuthRocket
124
112
  }, local_creds)
125
113
 
126
114
  session
127
- rescue JWT::ExpiredSignature, JWT::ImmatureSignature, JWT::InvalidAudError,
128
- JWT::InvalidIatError, JWT::InvalidIssuerError
129
- # successfully parsed, but invalid claims
115
+ rescue JWT::DecodeError
130
116
  nil
131
117
  end
132
118
 
133
119
  @_jwks ||= {}
134
120
  JWKS_MUTEX = Mutex.new
135
- MIN_ATTEMPT_WINDOW = 71 # seconds
136
121
 
137
122
  # private
138
- # use_cached - if there is a cached result, use it regardless of last cache load time
139
- def self.load_jwk_set(uri, use_cached:)
140
- keys, last_time = @_jwks.dig(uri, :keys), @_jwks.dig(uri, :time)
141
- last_time ||= 0
142
-
143
- return keys if use_cached && last_time > 0
144
- return keys if Time.now.to_f - MIN_ATTEMPT_WINDOW < last_time
145
-
123
+ def self.load_jwk_set(uri)
146
124
  JWKS_MUTEX.synchronize do
147
- # recheck in case we locked while being loaded in another process
148
- newer_keys, newer_time = @_jwks.dig(uri, :keys), @_jwks.dig(uri, :time)
149
- newer_time ||= 0
150
-
151
- return newer_keys if newer_time > last_time
152
-
153
125
  path = URI.parse(uri).path
154
126
  headers = build_headers({}, {})
155
127
  rest_opts = {
@@ -164,20 +136,12 @@ module AuthRocket
164
136
  response = execute_request(rest_opts)
165
137
  parsed = parse_response(response)
166
138
  # => {data: json, errors: errors, metadata: metadata}
167
- certs = parsed[:data][:keys].map do |h|
139
+ parsed[:data][:keys].each do |h|
168
140
  crt = "-----BEGIN PUBLIC KEY-----\n#{h['x5c'][0]}\n-----END PUBLIC KEY-----"
169
- OpenSSL::PKey.read crt
170
- end
171
-
172
- @_jwks[uri] = {keys: certs, time: Time.now.to_f}
173
- keys ||= []
174
- just_added = certs - keys
175
- if just_added.any?
176
- just_added
177
- else
178
- certs
141
+ @_jwks[h['kid']] = {secret: OpenSSL::PKey.read(crt), algo: h['alg']}
179
142
  end
180
143
  end
144
+ @_jwks
181
145
  end
182
146
 
183
147
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authrocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - AuthRocket Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-22 00:00:00.000000000 Z
11
+ date: 2020-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable