authrocket 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|