clowk 0.3.3 → 0.4.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 +4 -4
- data/lib/clowk/authenticable.rb +43 -5
- data/lib/clowk/configuration.rb +7 -0
- data/lib/clowk/http/client.rb +12 -2
- data/lib/clowk/middleware/token_extractor.rb +6 -0
- data/lib/clowk/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 724e8bb8826e85a7ca8a5ab0a35252a173ae351914318fdf1e7337b28d0acf24
|
|
4
|
+
data.tar.gz: '0649d40341028b61fa6bc6c5ec48f11d4426db5a3919e231b043b512df5d8676'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d641c9a88626dbadcd07e71aa433c34526c4e5de64e4727c593ceff50d227ca29ab8cee2352f0f69912e98b10dc4e341e81aa2382be49db192937a7b7fc7a3c
|
|
7
|
+
data.tar.gz: 6d896475327369927e31eb9ea02c40d635739b945cd01389a2431d2717030364d99fff4eac03b6f10a084a00c80dc0381b5b3d9cf55ce9f5337960b364a19152
|
data/lib/clowk/authenticable.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Clowk
|
|
|
13
13
|
signed_in_method = :"#{scope}_signed_in?"
|
|
14
14
|
|
|
15
15
|
enforce_session_method = :"#{scope}_enforce_session!"
|
|
16
|
+
sign_out_method = :"#{scope}_sign_out!"
|
|
16
17
|
|
|
17
18
|
base.class_eval do
|
|
18
19
|
unless current_method == :clowk_current_resource
|
|
@@ -39,6 +40,12 @@ module Clowk
|
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
43
|
+
unless sign_out_method == :clowk_sign_out!
|
|
44
|
+
define_method(sign_out_method) do
|
|
45
|
+
clowk_sign_out!
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
42
49
|
helper_method current_method, authenticate_method, signed_in_method, :current_token if respond_to?(:helper_method)
|
|
43
50
|
end
|
|
44
51
|
end
|
|
@@ -86,9 +93,17 @@ module Clowk
|
|
|
86
93
|
end
|
|
87
94
|
|
|
88
95
|
def clowk_authenticate!
|
|
89
|
-
return
|
|
96
|
+
return clowk_handle_unauthenticated unless clowk_signed_in?
|
|
97
|
+
|
|
98
|
+
# A valid token proves who signed in, not that the session still stands —
|
|
99
|
+
# revocation lives server-side. Opt in with config.enforce_active_session
|
|
100
|
+
# to pay a lookup (cached for session_status_ttl) on every authentication.
|
|
101
|
+
if Clowk.config.enforce_active_session
|
|
102
|
+
clowk_enforce_session!
|
|
103
|
+
return if respond_to?(:performed?) && performed?
|
|
104
|
+
end
|
|
90
105
|
|
|
91
|
-
|
|
106
|
+
clowk_current_resource
|
|
92
107
|
end
|
|
93
108
|
|
|
94
109
|
def clowk_sign_out!
|
|
@@ -127,8 +142,13 @@ module Clowk
|
|
|
127
142
|
nil
|
|
128
143
|
end
|
|
129
144
|
|
|
145
|
+
# Bearer header or cookie only — never the query string. A valid token in a
|
|
146
|
+
# URL would otherwise sign the visitor in on ANY path, bypassing the state
|
|
147
|
+
# check that makes the OAuth callback safe, and would stay replayable for the
|
|
148
|
+
# token's full lifetime anywhere the URL was logged. CallbacksController
|
|
149
|
+
# reads params directly, after validating state.
|
|
130
150
|
def extracted_token
|
|
131
|
-
@extracted_token ||= Clowk::Middleware::TokenExtractor.new(request).call
|
|
151
|
+
@extracted_token ||= Clowk::Middleware::TokenExtractor.new(request, token_param: nil).call
|
|
132
152
|
end
|
|
133
153
|
|
|
134
154
|
def stored_session
|
|
@@ -161,7 +181,7 @@ module Clowk
|
|
|
161
181
|
def resolve_session_status
|
|
162
182
|
cached = stored_session&.dig("session_status") || stored_session&.dig(:session_status)
|
|
163
183
|
|
|
164
|
-
return cached&.deep_symbolize_keys if cached
|
|
184
|
+
return cached&.deep_symbolize_keys if cached && clowk_session_status_fresh?
|
|
165
185
|
|
|
166
186
|
resource = clowk_current_resource
|
|
167
187
|
|
|
@@ -172,11 +192,29 @@ module Clowk
|
|
|
172
192
|
result = client.tokens.verify_with_session(token: current_token)
|
|
173
193
|
status = result&.dig(:session)
|
|
174
194
|
|
|
175
|
-
|
|
195
|
+
if status && stored_session
|
|
196
|
+
session[Clowk.config.session_key] = stored_session.merge(
|
|
197
|
+
"session_status" => status,
|
|
198
|
+
"session_status_checked_at" => Time.now.to_i
|
|
199
|
+
)
|
|
200
|
+
end
|
|
176
201
|
|
|
177
202
|
status
|
|
178
203
|
rescue Clowk::InvalidTokenError
|
|
179
204
|
nil
|
|
180
205
|
end
|
|
206
|
+
|
|
207
|
+
# Whether the cached status may still be trusted. Timestamped alongside the
|
|
208
|
+
# payload rather than inside it, so what we hand back stays exactly what the
|
|
209
|
+
# API returned.
|
|
210
|
+
def clowk_session_status_fresh?
|
|
211
|
+
ttl = Clowk.config.session_status_ttl.to_i
|
|
212
|
+
return false unless ttl.positive?
|
|
213
|
+
|
|
214
|
+
checked_at = (stored_session&.dig("session_status_checked_at") ||
|
|
215
|
+
stored_session&.dig(:session_status_checked_at)).to_i
|
|
216
|
+
|
|
217
|
+
checked_at.positive? && (Time.now.to_i - checked_at) < ttl
|
|
218
|
+
end
|
|
181
219
|
end
|
|
182
220
|
end
|
data/lib/clowk/configuration.rb
CHANGED
|
@@ -22,6 +22,7 @@ module Clowk
|
|
|
22
22
|
attr_accessor :token_param
|
|
23
23
|
attr_accessor :enforce_active_session
|
|
24
24
|
attr_accessor :on_session_expired
|
|
25
|
+
attr_accessor :session_status_ttl
|
|
25
26
|
|
|
26
27
|
def initialize
|
|
27
28
|
@api_base_url = "https://api.clowk.dev/api/v1"
|
|
@@ -43,6 +44,11 @@ module Clowk
|
|
|
43
44
|
@token_param = :token
|
|
44
45
|
@enforce_active_session = false
|
|
45
46
|
@on_session_expired = nil
|
|
47
|
+
# How long a fetched session status stays trusted, in seconds. Without a
|
|
48
|
+
# TTL the first lookup would be cached for the life of the Rails session,
|
|
49
|
+
# which silently turns every later enforcement call into a no-op. Set 0 to
|
|
50
|
+
# check on every call.
|
|
51
|
+
@session_status_ttl = 300
|
|
46
52
|
end
|
|
47
53
|
|
|
48
54
|
def after_sign_in_path
|
|
@@ -64,6 +70,7 @@ module Clowk
|
|
|
64
70
|
errors << "http_read_timeout must be Numeric" unless @http_read_timeout.is_a?(Numeric)
|
|
65
71
|
errors << "http_write_timeout must be Numeric" unless @http_write_timeout.is_a?(Numeric)
|
|
66
72
|
errors << "http_retry_attempts must be a non-negative Integer" unless @http_retry_attempts.is_a?(Integer) && @http_retry_attempts >= 0
|
|
73
|
+
errors << "session_status_ttl must be a non-negative Integer" unless @session_status_ttl.is_a?(Integer) && @session_status_ttl >= 0
|
|
67
74
|
|
|
68
75
|
raise ConfigurationError, errors.join(", ") if errors.any?
|
|
69
76
|
|
data/lib/clowk/http/client.rb
CHANGED
|
@@ -137,12 +137,22 @@ module Clowk
|
|
|
137
137
|
|
|
138
138
|
def build_uri(path)
|
|
139
139
|
base_uri = URI(base_url)
|
|
140
|
-
|
|
141
|
-
base_uri.
|
|
140
|
+
request_path, request_query = split_query(path)
|
|
141
|
+
base_uri.path = join_paths(base_uri.path, normalize_path(request_path))
|
|
142
|
+
base_uri.query = request_query
|
|
142
143
|
base_uri.fragment = nil
|
|
143
144
|
base_uri
|
|
144
145
|
end
|
|
145
146
|
|
|
147
|
+
# A request path may arrive with its query already attached (the SDK's
|
|
148
|
+
# `search` builds "resource/search?query=..."). URI#path= rejects a value
|
|
149
|
+
# containing "?", so split the query off and set it as its own component
|
|
150
|
+
# instead of smuggling it through the path.
|
|
151
|
+
def split_query(path)
|
|
152
|
+
path_part, _separator, query_part = path.to_s.partition("?")
|
|
153
|
+
[path_part, query_part.empty? ? nil : query_part]
|
|
154
|
+
end
|
|
155
|
+
|
|
146
156
|
def normalize_path(path)
|
|
147
157
|
path.to_s.start_with?("/") ? path : "/#{path}"
|
|
148
158
|
end
|
|
@@ -17,7 +17,13 @@ module Clowk
|
|
|
17
17
|
|
|
18
18
|
attr_reader :request, :token_param, :cookie_key
|
|
19
19
|
|
|
20
|
+
# Opt-in only. Reading a token from the query string on every request lets
|
|
21
|
+
# a link like /anything?token=<jwt> establish a session without ever
|
|
22
|
+
# passing the callback's `state` check, so callers that establish sessions
|
|
23
|
+
# pass token_param: nil. The callback reads params itself, after state.
|
|
20
24
|
def token_from_params
|
|
25
|
+
return if token_param.nil?
|
|
26
|
+
|
|
21
27
|
params = (request.respond_to?(:params) && request.params) ? request.params : {}
|
|
22
28
|
params[token_param.to_s].presence
|
|
23
29
|
end
|
data/lib/clowk/version.rb
CHANGED