clowk 0.7.1 → 0.9.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/README.md +34 -6
- data/lib/clowk/authenticable.rb +32 -22
- data/lib/clowk/configuration.rb +20 -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: a1fa17ef38ffc2cce253e598ab2c2478ba05f5f7c638601ec833bafa00377ebc
|
|
4
|
+
data.tar.gz: 396b0e5c36dcaa01a10397f6227af24dcdf5274a06145222da54855aba71a076
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1fd3f7de008dd50644494e85e0771113ae98be1efdc1abcae7d97888b6af84f7aa2f5fd086aa711d3be4b2984b0a626c9bdddc5d66c6c8bc41955b21325c3717
|
|
7
|
+
data.tar.gz: 36067b774a1d57d833909bcee4316d29a25ee5a907b03063f8ee52545adbf36b09a9857b4b374065a60956f2a5a386044138592b9643e0cf5a7b2f48dec50da8
|
data/README.md
CHANGED
|
@@ -239,15 +239,18 @@ those actions and they get a live one:
|
|
|
239
239
|
|
|
240
240
|
```ruby
|
|
241
241
|
class ApiKeysController < ApplicationController
|
|
242
|
-
|
|
242
|
+
before_action :clowk_enforce_fresh_session!, only: [:create, :update, :destroy]
|
|
243
243
|
end
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
cached check, so an app pays for the round trip on
|
|
248
|
-
undo and nowhere else.
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
An ordinary `before_action`, so the filter options you already know all work.
|
|
247
|
+
Everything not named keeps the cached check, so an app pays for the round trip on
|
|
248
|
+
the few actions it cannot undo and nowhere else.
|
|
249
|
+
|
|
250
|
+
Under a configured `prefix_by` the method is named for the scope, like every
|
|
251
|
+
other one here — `clowk_user_enforce_fresh_session!` beside
|
|
252
|
+
`clowk_user_enforce_session!`. And `clowk_session_active?(force: true)` returns
|
|
253
|
+
the answer instead of enforcing it.
|
|
251
254
|
|
|
252
255
|
Two settings decide what happens when Clowk itself cannot be reached:
|
|
253
256
|
|
|
@@ -269,6 +272,31 @@ with no round trip at all.
|
|
|
269
272
|
Both ends — the broker said inactive, the ceiling passed — go through
|
|
270
273
|
`config.on_session_expired` when you set one.
|
|
271
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
|
+
```ruby
|
|
281
|
+
config.token_store = :cookie # Clowk's cookie only
|
|
282
|
+
config.token_store = :session # also mirrored into the session (the default)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The copy is not free. A Rails session lives in **one** cookie with about 4096
|
|
286
|
+
bytes to its name, and in production, where tokens are RS256, the token is most
|
|
287
|
+
of what a session weighs. Hand a browser more than it will hold and it discards
|
|
288
|
+
the whole cookie in silence — no error server-side, none in the console, the
|
|
289
|
+
previous cookie simply stays. Everything written on that request goes with it: a
|
|
290
|
+
flash message, a selected tenant, a CSRF rotation. What a person sees is a button
|
|
291
|
+
that does nothing.
|
|
292
|
+
|
|
293
|
+
Under `:cookie` the session keeps the claims and the sign-in time, nothing else.
|
|
294
|
+
`current_token` reads Clowk's cookie instead, which is where an API-only app has
|
|
295
|
+
always read it from. Sessions written before the switch are pruned on their next
|
|
296
|
+
request, so an app does not have to wait for everyone to sign out.
|
|
297
|
+
|
|
298
|
+
The default stays `:session` so an upgrade changes nothing until you ask.
|
|
299
|
+
|
|
272
300
|
### Token verification
|
|
273
301
|
|
|
274
302
|
Tokens signed with `RS256` are verified against Clowk's public key set, fetched
|
data/lib/clowk/authenticable.rb
CHANGED
|
@@ -67,22 +67,6 @@ module Clowk
|
|
|
67
67
|
Clowk::Authenticable.install_dynamic_methods(self)
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
class_methods do
|
|
71
|
-
# Demand a live answer from Clowk before these actions, whatever a cached
|
|
72
|
-
# status says.
|
|
73
|
-
#
|
|
74
|
-
# class ApiKeysController < ApplicationController
|
|
75
|
-
# clowk_require_fresh_session only: [:create, :update, :destroy]
|
|
76
|
-
# end
|
|
77
|
-
#
|
|
78
|
-
# Takes the same options as before_action. Everything NOT listed keeps the
|
|
79
|
-
# cached check, which is the point: an app pays for a round trip on the few
|
|
80
|
-
# actions that cannot be undone, and nowhere else.
|
|
81
|
-
def clowk_require_fresh_session(**options)
|
|
82
|
-
before_action(**options) { clowk_enforce_fresh_session! }
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
70
|
# Per-request credentials — for apps whose keys are not a boot constant:
|
|
87
71
|
# an operator pastes a publishable key into a settings screen, or one
|
|
88
72
|
# process serves several tenants.
|
|
@@ -106,10 +90,17 @@ module Clowk
|
|
|
106
90
|
def clowk_current_resource
|
|
107
91
|
@clowk_current_resource ||= begin
|
|
108
92
|
payload = stored_user_payload || verified_request_payload
|
|
93
|
+
|
|
94
|
+
drop_mirrored_token!
|
|
95
|
+
|
|
109
96
|
payload ? Current.new(payload) : nil
|
|
110
97
|
end
|
|
111
98
|
end
|
|
112
99
|
|
|
100
|
+
# The session first, which holds a copy only under token_store :session —
|
|
101
|
+
# and, for one request after an app switches to :cookie, in a session
|
|
102
|
+
# written before the switch. drop_mirrored_token! clears those. Clowk's own
|
|
103
|
+
# cookie is the source either way.
|
|
113
104
|
def current_token
|
|
114
105
|
stored_session&.dig("token") || extracted_token
|
|
115
106
|
end
|
|
@@ -143,7 +134,10 @@ module Clowk
|
|
|
143
134
|
# rotating a secret, deleting an account, removing a member. Everything else
|
|
144
135
|
# should take the cached check — this is a round trip, on purpose.
|
|
145
136
|
#
|
|
146
|
-
#
|
|
137
|
+
# before_action :clowk_enforce_fresh_session!, only: [:destroy]
|
|
138
|
+
#
|
|
139
|
+
# Under a configured prefix_by it is named for the scope, like every other
|
|
140
|
+
# method here: `clowk_user_enforce_fresh_session!`.
|
|
147
141
|
#
|
|
148
142
|
# Before 0.7 the only way to get this was `session_status_ttl = 0`, which
|
|
149
143
|
# bought freshness here by paying a round trip on every page instead.
|
|
@@ -324,11 +318,10 @@ module Clowk
|
|
|
324
318
|
store = clowk_session_store
|
|
325
319
|
return if store.nil?
|
|
326
320
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
321
|
+
claims = {user: payload, signed_in_at: Time.now.to_i}
|
|
322
|
+
|
|
323
|
+
store[Clowk.config.session_key] =
|
|
324
|
+
(Clowk.config.token_store == :cookie) ? claims : claims.merge(token:)
|
|
332
325
|
|
|
333
326
|
clowk_cookie_jar&.[]=(Clowk.config.cookie_key, {
|
|
334
327
|
value: token,
|
|
@@ -338,6 +331,23 @@ module Clowk
|
|
|
338
331
|
})
|
|
339
332
|
end
|
|
340
333
|
|
|
334
|
+
# A session written under :session 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 :cookie 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 == :cookie
|
|
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
|
+
|
|
341
351
|
def resolve_session_status(force: false)
|
|
342
352
|
@clowk_session_check_unavailable = false
|
|
343
353
|
cached = force ? nil : clowk_read_cached_session_status
|
data/lib/clowk/configuration.rb
CHANGED
|
@@ -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,25 @@ 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
|
+
# Where the token is kept for the next request: :session or :cookie.
|
|
70
|
+
#
|
|
71
|
+
# Clowk's own cookie is written either way — it is what `current_token`
|
|
72
|
+
# reads when the session has none, and the only place an API-only app has
|
|
73
|
+
# ever had. The setting decides whether a COPY is also mirrored into the
|
|
74
|
+
# app's Rails session, as every version before 0.9 did.
|
|
75
|
+
#
|
|
76
|
+
# That copy is not free. A Rails session lives in ONE cookie with about
|
|
77
|
+
# 4096 bytes to its name, and in production, where tokens are RS256, the
|
|
78
|
+
# token is most of what a session weighs. Hand a browser more than it will
|
|
79
|
+
# hold and it discards the whole cookie in silence: no error server-side,
|
|
80
|
+
# none in the console, the previous cookie simply stays. Everything written
|
|
81
|
+
# on that request goes with it — a flash message, a selected tenant — which
|
|
82
|
+
# reads as a button that does nothing.
|
|
83
|
+
#
|
|
84
|
+
# :session stays the default so an upgrade changes nothing until an app
|
|
85
|
+
# asks for :cookie.
|
|
86
|
+
@token_store = :session
|
|
67
87
|
end
|
|
68
88
|
|
|
69
89
|
# Where API-only apps cache session status, since they have no Rails session
|
data/lib/clowk/version.rb
CHANGED