mbuzz 0.7.2 → 0.7.3
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/CHANGELOG.md +20 -0
- data/lib/mbuzz/middleware/tracking.rb +33 -36
- data/lib/mbuzz/version.rb +1 -1
- data/lib/mbuzz.rb +0 -3
- 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: 717fff0f77150fb5f0a2ddcd60e59aaccaacdddbe41acd88ec7b953b568ec448
|
|
4
|
+
data.tar.gz: caedfee3a6e6ced3dcfadcfcce5b80a9f13679b73710f0933b6c2d8573c2accb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce5da1805916ac523dc3d9a40e8b3126ee8f7b1d95cc3fe042d6e800e796e38227f117edd9acb72e1ec4e357cd83d378fe87b3f87eb063c2857f267ae2508c56
|
|
7
|
+
data.tar.gz: 190f2ddbf15dbc17cde637ee4f8ed4d00a8b06d47f0d16fce9b54e96e67e7e8191359f57cc3a5e218c9028ccac072e14e0035a1c1cc0f5c9a4e95b3c14b73c41
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.3] - 2026-02-02
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
|
|
12
|
+
- **Session cookie removed** — `_mbuzz_sid` cookie is no longer set or read. Sessions are fully server-side.
|
|
13
|
+
- **`SESSION_COOKIE_NAME` and `SESSION_COOKIE_MAX_AGE` constants removed** from `Mbuzz` module.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Navigation-aware session creation** — middleware now gates `POST /sessions` on real page navigations using browser-enforced `Sec-Fetch-*` headers (whitelist), with a framework-specific blacklist fallback for older browsers and bots.
|
|
18
|
+
- Turbo Frame, htmx, Unpoly, and XHR sub-requests no longer create spurious sessions.
|
|
19
|
+
- Prefetches and iframe loads are correctly filtered out.
|
|
20
|
+
- New public methods on `Mbuzz::Middleware::Tracking`: `should_create_session?`, `sec_fetch_headers?`, `page_navigation?`, `framework_sub_request?`
|
|
21
|
+
|
|
22
|
+
### Migration Guide
|
|
23
|
+
|
|
24
|
+
1. Remove any code that reads or depends on the `_mbuzz_sid` cookie.
|
|
25
|
+
2. Remove references to `Mbuzz::SESSION_COOKIE_NAME` or `Mbuzz::SESSION_COOKIE_MAX_AGE`.
|
|
26
|
+
3. Visitor cookie (`_mbuzz_vid`) is unaffected — still set on every request.
|
|
27
|
+
|
|
8
28
|
## [0.7.0] - 2026-01-09
|
|
9
29
|
|
|
10
30
|
### Breaking Changes
|
|
@@ -23,12 +23,11 @@ module Mbuzz
|
|
|
23
23
|
|
|
24
24
|
store_in_current_attributes(context, request)
|
|
25
25
|
|
|
26
|
-
create_session_async(context, request) if
|
|
26
|
+
create_session_async(context, request) if should_create_session?(env)
|
|
27
27
|
|
|
28
28
|
RequestContext.with_context(request: request) do
|
|
29
29
|
status, headers, body = @app.call(env)
|
|
30
30
|
set_visitor_cookie(headers, context, request)
|
|
31
|
-
set_session_cookie(headers, context, request)
|
|
32
31
|
[status, headers, body]
|
|
33
32
|
ensure
|
|
34
33
|
reset_current_attributes
|
|
@@ -51,25 +50,46 @@ module Mbuzz
|
|
|
51
50
|
Mbuzz.config.all_skip_extensions.any? { |ext| path.end_with?(ext) }
|
|
52
51
|
end
|
|
53
52
|
|
|
53
|
+
# Navigation detection — only create sessions for real page navigations.
|
|
54
|
+
# Sec-Fetch-* headers (browser-enforced, unforgeable) are the primary signal;
|
|
55
|
+
# framework-specific blacklist covers old browsers and bots.
|
|
56
|
+
|
|
57
|
+
def should_create_session?(env)
|
|
58
|
+
return page_navigation?(env) if sec_fetch_headers?(env)
|
|
59
|
+
|
|
60
|
+
!framework_sub_request?(env)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def sec_fetch_headers?(env)
|
|
64
|
+
!env["HTTP_SEC_FETCH_MODE"].nil?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def page_navigation?(env)
|
|
68
|
+
env["HTTP_SEC_FETCH_MODE"] == "navigate" &&
|
|
69
|
+
env["HTTP_SEC_FETCH_DEST"] == "document" &&
|
|
70
|
+
env["HTTP_SEC_PURPOSE"].nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def framework_sub_request?(env)
|
|
74
|
+
!env["HTTP_TURBO_FRAME"].nil? ||
|
|
75
|
+
!env["HTTP_HX_REQUEST"].nil? ||
|
|
76
|
+
!env["HTTP_X_UP_VERSION"].nil? ||
|
|
77
|
+
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
|
|
78
|
+
end
|
|
79
|
+
|
|
54
80
|
private
|
|
55
81
|
|
|
56
|
-
# Build all request-specific context as a frozen hash
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# NOT accessed from the request object in the background thread (see #create_session)
|
|
82
|
+
# Build all request-specific context as a frozen hash.
|
|
83
|
+
# Thread-safety: all values needed by async session creation are captured here,
|
|
84
|
+
# NOT accessed from the request object in the background thread.
|
|
60
85
|
def build_request_context(request)
|
|
61
|
-
existing_session_id = session_id_from_cookie(request)
|
|
62
|
-
new_session = existing_session_id.nil?
|
|
63
86
|
ip = extract_ip(request)
|
|
64
87
|
user_agent = request.user_agent.to_s
|
|
65
88
|
|
|
66
89
|
{
|
|
67
90
|
visitor_id: resolve_visitor_id(request),
|
|
68
|
-
session_id:
|
|
91
|
+
session_id: SecureRandom.uuid,
|
|
69
92
|
user_id: user_id_from_session(request),
|
|
70
|
-
new_session: new_session,
|
|
71
|
-
# Session creation data - captured here for thread-safety
|
|
72
|
-
# Background thread must NOT read from request object
|
|
73
93
|
url: request.url,
|
|
74
94
|
referrer: request.referer,
|
|
75
95
|
ip: ip,
|
|
@@ -86,14 +106,6 @@ module Mbuzz
|
|
|
86
106
|
request.cookies[VISITOR_COOKIE_NAME]
|
|
87
107
|
end
|
|
88
108
|
|
|
89
|
-
def session_id_from_cookie(request)
|
|
90
|
-
request.cookies[SESSION_COOKIE_NAME]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def generate_session_id
|
|
94
|
-
SecureRandom.uuid
|
|
95
|
-
end
|
|
96
|
-
|
|
97
109
|
def user_id_from_session(request)
|
|
98
110
|
request.session[SESSION_USER_ID_KEY] if request.session
|
|
99
111
|
end
|
|
@@ -126,7 +138,7 @@ module Mbuzz
|
|
|
126
138
|
Rails.logger.error("[Mbuzz] #{message}")
|
|
127
139
|
end
|
|
128
140
|
|
|
129
|
-
# Cookie setting - visitor
|
|
141
|
+
# Cookie setting - visitor identity only (sessions are server-side)
|
|
130
142
|
|
|
131
143
|
def set_visitor_cookie(headers, context, request)
|
|
132
144
|
Rack::Utils.set_cookie_header!(
|
|
@@ -136,14 +148,6 @@ module Mbuzz
|
|
|
136
148
|
)
|
|
137
149
|
end
|
|
138
150
|
|
|
139
|
-
def set_session_cookie(headers, context, request)
|
|
140
|
-
Rack::Utils.set_cookie_header!(
|
|
141
|
-
headers,
|
|
142
|
-
SESSION_COOKIE_NAME,
|
|
143
|
-
session_cookie_options(context, request)
|
|
144
|
-
)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
151
|
def visitor_cookie_options(context, request)
|
|
148
152
|
base_cookie_options(request).merge(
|
|
149
153
|
value: context[:visitor_id],
|
|
@@ -151,13 +155,6 @@ module Mbuzz
|
|
|
151
155
|
)
|
|
152
156
|
end
|
|
153
157
|
|
|
154
|
-
def session_cookie_options(context, request)
|
|
155
|
-
base_cookie_options(request).merge(
|
|
156
|
-
value: context[:session_id],
|
|
157
|
-
max_age: SESSION_COOKIE_MAX_AGE
|
|
158
|
-
)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
158
|
def base_cookie_options(request)
|
|
162
159
|
options = {
|
|
163
160
|
path: VISITOR_COOKIE_PATH,
|
data/lib/mbuzz/version.rb
CHANGED
data/lib/mbuzz.rb
CHANGED
|
@@ -27,9 +27,6 @@ module Mbuzz
|
|
|
27
27
|
VISITOR_COOKIE_PATH = "/"
|
|
28
28
|
VISITOR_COOKIE_SAME_SITE = "Lax"
|
|
29
29
|
|
|
30
|
-
SESSION_COOKIE_NAME = "_mbuzz_sid"
|
|
31
|
-
SESSION_COOKIE_MAX_AGE = 30 * 60 # 30 minutes
|
|
32
|
-
|
|
33
30
|
SESSION_USER_ID_KEY = "user_id"
|
|
34
31
|
ENV_USER_ID_KEY = "mbuzz.user_id"
|
|
35
32
|
ENV_VISITOR_ID_KEY = "mbuzz.visitor_id"
|