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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b02c176f355917aa070a2b183a5844911e769b49ff7906cc40c13bbcd68cbf6
4
- data.tar.gz: 7ba591ce54755e2ff3d6a586ef7942ae96442efa6ae93caff5c64e0ac313cbd1
3
+ metadata.gz: 724e8bb8826e85a7ca8a5ab0a35252a173ae351914318fdf1e7337b28d0acf24
4
+ data.tar.gz: '0649d40341028b61fa6bc6c5ec48f11d4426db5a3919e231b043b512df5d8676'
5
5
  SHA512:
6
- metadata.gz: f33e323f94c88fc064d12d0caa0cbc87bf9fad301ba0e29fc89d51ec0f40e81658e7dc67f485e437281705b0cdd10c6157b927500d0afd255f151d48485188ad
7
- data.tar.gz: 9c6e02049ac043a85e293ad4ac8ec060d4f90e2b1231931cb593a6287a73b1400bdb6c4c01e18cf20fe090cf5be43336eeade52bb495c8d24074878b0a4c688f
6
+ metadata.gz: 9d641c9a88626dbadcd07e71aa433c34526c4e5de64e4727c593ceff50d227ca29ab8cee2352f0f69912e98b10dc4e341e81aa2382be49db192937a7b7fc7a3c
7
+ data.tar.gz: 6d896475327369927e31eb9ea02c40d635739b945cd01389a2431d2717030364d99fff4eac03b6f10a084a00c80dc0381b5b3d9cf55ce9f5337960b364a19152
@@ -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 clowk_current_resource if clowk_signed_in?
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
- clowk_handle_unauthenticated
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
- session[Clowk.config.session_key] = stored_session.merge("session_status" => status) if status && stored_session
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
@@ -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
 
@@ -137,12 +137,22 @@ module Clowk
137
137
 
138
138
  def build_uri(path)
139
139
  base_uri = URI(base_url)
140
- base_uri.path = join_paths(base_uri.path, normalize_path(path))
141
- base_uri.query = nil
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clowk
4
- VERSION = "0.3.3"
4
+ VERSION = "0.4.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clowk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clowk