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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +1 -1
- data/lib/authrocket/api/version.rb +1 -1
- data/lib/authrocket/rails/controller_helper.rb +8 -0
- data/lib/authrocket/rails/engine.rb +1 -0
- data/lib/authrocket/session.rb +17 -53
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82ed5a2ea3b1c412a72c84aeac220c4b54e26ca5a0f9cc7248ecbca9e3c211bb
|
4
|
+
data.tar.gz: 5acc9665f07016d75af2f26d08527206749cc7e4bfe56df6df248e50a3d9fe5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 375bcc4653c32f973da17893ff5250131c655d7122a4ba88a3d818fc454ebfdc7c9561db996c67bb6dec141823dceb1975124b8a0714eae448ac24965ae7d2e0
|
7
|
+
data.tar.gz: b12e98876190ded64592edc86c2fd160dd8b72f75086617f72164b3f7aef90f0dc2bb6fc62195eb8e2a0b375bda7f93a1a517fdeff421fbf2b8c0d0912bd47b3
|
data/CHANGELOG.md
CHANGED
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://
|
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
|
|
@@ -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)
|
data/lib/authrocket/session.rb
CHANGED
@@ -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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
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
|
-
#
|
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::
|
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
|
-
|
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
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2020-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|