clowk 0.8.0 → 0.9.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: b8925255001baaeae3fb7507651c4fa65ba8ccb24358a593c191f5e3a0070b2a
4
- data.tar.gz: f6acd810ff58c1410c67c8b78c4db705966fa8643d39fef2673da25816c006c8
3
+ metadata.gz: 28abd896caa982a35fa6efd1f1412ce35c65e16c097ae6bca4106f7947eb235b
4
+ data.tar.gz: 6f34617088b87ac8c7e7a1b9792e772a62134e78726ee750ec4dc878e87f5cf7
5
5
  SHA512:
6
- metadata.gz: d61ceac58f3444d2cf6cf0af582d595a115c0971048ad7a2e979611cc45a4020bbc1a3689fccf33144ccc3d28bbc7cfbc32cb1bd870de94f43cede1289cd9aea
7
- data.tar.gz: 538f167b4d02d928f9cd231fadbbb8e8a7639b2648903e5f46da1414b058b376e5dd9ce926ab47cbb1fbc1aeacbdce748d5ef8bb6d24ea8c6ec00fd90f941685
6
+ metadata.gz: 9b35e25266da57b3c408ef66b3386d2fd85cc1c5aa6f0aafe9df0cac2b52e365b68dc79219f6f6857845043725e35b3e1c1939a37ee57d38206bc63fca7629d2
7
+ data.tar.gz: afc0e6ed3b78bcc9f7caed60cbda3131ad3869e141ed999a8588e95525ea52f8f5bea32624c2982e2d76a40206618d707978a8830f235b1a906bdc85271aa93e
data/README.md CHANGED
@@ -272,6 +272,33 @@ with no round trip at all.
272
272
  Both ends — the broker said inactive, the ceiling passed — go through
273
273
  `config.on_session_expired` when you set one.
274
274
 
275
+ ### Where the token is kept
276
+
277
+ Clowk writes the token to its own cookie, and — by default, as every version
278
+ before 0.9 did — mirrors a copy into the app's Rails session.
279
+
280
+ Both are cookies, so the setting names the owner rather than the mechanism:
281
+
282
+ ```ruby
283
+ config.token_store = :clowk # Clowk's own cookie, and nowhere else
284
+ config.token_store = :app # also mirrored into the Rails session (the default)
285
+ ```
286
+
287
+ The copy is not free. A Rails session lives in **one** cookie with about 4096
288
+ bytes to its name, and in production, where tokens are RS256, the token is most
289
+ of what a session weighs. Hand a browser more than it will hold and it discards
290
+ the whole cookie in silence — no error server-side, none in the console, the
291
+ previous cookie simply stays. Everything written on that request goes with it: a
292
+ flash message, a selected tenant, a CSRF rotation. What a person sees is a button
293
+ that does nothing.
294
+
295
+ Under `:clowk` the session keeps the claims and the sign-in time, nothing else.
296
+ `current_token` reads Clowk's cookie instead, which is where an API-only app has
297
+ always read it from. Sessions written before the switch are pruned on their next
298
+ request, so an app does not have to wait for everyone to sign out.
299
+
300
+ The default stays `:app` so an upgrade changes nothing until you ask.
301
+
275
302
  ### Token verification
276
303
 
277
304
  Tokens signed with `RS256` are verified against Clowk's public key set, fetched
@@ -90,10 +90,17 @@ module Clowk
90
90
  def clowk_current_resource
91
91
  @clowk_current_resource ||= begin
92
92
  payload = stored_user_payload || verified_request_payload
93
+
94
+ drop_mirrored_token!
95
+
93
96
  payload ? Current.new(payload) : nil
94
97
  end
95
98
  end
96
99
 
100
+ # The session first, which holds a copy only under token_store :app —
101
+ # and, for one request after an app switches to :clowk, in a session
102
+ # written before the switch. drop_mirrored_token! clears those. Clowk's own
103
+ # cookie is the source either way.
97
104
  def current_token
98
105
  stored_session&.dig("token") || extracted_token
99
106
  end
@@ -311,11 +318,10 @@ module Clowk
311
318
  store = clowk_session_store
312
319
  return if store.nil?
313
320
 
314
- store[Clowk.config.session_key] = {
315
- token:,
316
- user: payload,
317
- signed_in_at: Time.now.to_i
318
- }
321
+ claims = {user: payload, signed_in_at: Time.now.to_i}
322
+
323
+ store[Clowk.config.session_key] =
324
+ (Clowk.config.token_store == :clowk) ? claims : claims.merge(token:)
319
325
 
320
326
  clowk_cookie_jar&.[]=(Clowk.config.cookie_key, {
321
327
  value: token,
@@ -325,6 +331,23 @@ module Clowk
325
331
  })
326
332
  end
327
333
 
334
+ # A session written under :app still carries the token, and
335
+ # persist_clowk_session does not run again while that session stands — so
336
+ # without this, switching an app to :clowk would shrink nothing until every
337
+ # person signed out and back in. Drops the copy once, on the first request
338
+ # after the switch.
339
+ def drop_mirrored_token!
340
+ return unless Clowk.config.token_store == :clowk
341
+
342
+ store = clowk_session_store
343
+ held = stored_session
344
+
345
+ return if store.nil? || held.nil?
346
+ return unless held.key?("token") || held.key?(:token)
347
+
348
+ store[Clowk.config.session_key] = held.except("token", :token)
349
+ end
350
+
328
351
  def resolve_session_status(force: false)
329
352
  @clowk_session_check_unavailable = false
330
353
  cached = force ? nil : clowk_read_cached_session_status
@@ -27,6 +27,7 @@ module Clowk
27
27
  attr_accessor :session_status_ttl
28
28
  attr_accessor :max_session_age
29
29
  attr_accessor :fail_open_on_broker_error
30
+ attr_accessor :token_store
30
31
  attr_writer :session_status_cache
31
32
 
32
33
  def initialize
@@ -64,6 +65,30 @@ module Clowk
64
65
  # cannot be made at all, the session is left standing and checked again on
65
66
  # the next request; max_session_age is what bounds that.
66
67
  @fail_open_on_broker_error = true
68
+
69
+ # Whose cookie keeps the token for the next request: :app or :clowk.
70
+ #
71
+ # Both are cookies — that is why the setting names the OWNER rather than
72
+ # the mechanism. :clowk is this gem's own cookie; :app is the Rails
73
+ # session, which is itself one cookie carrying everything the app puts in
74
+ # `session[...]`.
75
+ #
76
+ # Clowk's own cookie is written either way — it is what `current_token`
77
+ # reads when the session has none, and the only place an API-only app has
78
+ # ever had. The setting decides whether a COPY is also mirrored into the
79
+ # app's Rails session, as every version before 0.9 did.
80
+ #
81
+ # That copy is not free. A Rails session lives in ONE cookie with about
82
+ # 4096 bytes to its name, and in production, where tokens are RS256, the
83
+ # token is most of what a session weighs. Hand a browser more than it will
84
+ # hold and it discards the whole cookie in silence: no error server-side,
85
+ # none in the console, the previous cookie simply stays. Everything written
86
+ # on that request goes with it — a flash message, a selected tenant — which
87
+ # reads as a button that does nothing.
88
+ #
89
+ # :session stays the default so an upgrade changes nothing until an app
90
+ # asks for :cookie.
91
+ @token_store = :app
67
92
  end
68
93
 
69
94
  # Where API-only apps cache session status, since they have no Rails session
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.8.0"
4
+ VERSION = "0.9.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.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clowk