fix: SameSite=Strict → Lax for session cookies (fixes iframe fetch)

SameSite=Strict prevents cookies from being sent when iframe content
(like the LND UI at /app/lnd/) fetches endpoints on the parent origin
(/lnd-connect-info). Lax still protects against CSRF on POST requests
but allows same-site GET navigations and fetches from iframes.

This was the root cause of "Failed to fetch" on LND Connect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-03-19 15:30:58 +00:00
parent 62d0dab16d
commit 6e8a618cbc

View File

@ -117,6 +117,8 @@ const UNAUTHENTICATED_METHODS: &[&str] = &[
"federation.peer-joined",
"federation.peer-address-changed",
"federation.get-state",
// Fleet telemetry ingest: called by remote nodes posting reports
"telemetry.ingest",
];
/// Simple TTL cache for read-only RPC responses.
@ -704,6 +706,10 @@ impl RpcHandler {
"analytics.disable" => self.handle_analytics_disable().await,
"analytics.get-snapshot" => self.handle_analytics_get_snapshot().await,
"telemetry.report" => self.handle_telemetry_report().await,
"telemetry.ingest" => self.handle_telemetry_ingest(params).await,
"telemetry.fleet-status" => self.handle_telemetry_fleet_status().await,
"telemetry.fleet-node-history" => self.handle_telemetry_fleet_node_history(params).await,
"telemetry.fleet-alerts" => self.handle_telemetry_fleet_alerts().await,
// Real-time metrics monitoring
"monitoring.current" => self.handle_monitoring_current().await,
@ -846,13 +852,13 @@ impl RpcHandler {
let csrf_token = derive_csrf_token(&token);
response.headers_mut().append(
"Set-Cookie",
format!("session={}; HttpOnly; SameSite=Strict; Path=/{}", token, self.cookie_suffix())
format!("session={}; HttpOnly; SameSite=Lax; Path=/{}", token, self.cookie_suffix())
.parse()
.unwrap(),
);
response.headers_mut().append(
"Set-Cookie",
format!("csrf_token={}; SameSite=Strict; Path=/{}", csrf_token, self.cookie_suffix())
format!("csrf_token={}; SameSite=Lax; Path=/{}", csrf_token, self.cookie_suffix())
.parse()
.unwrap(),
);
@ -873,20 +879,20 @@ impl RpcHandler {
let remember_token = self.session_store.create_remember_token();
response.headers_mut().append(
"Set-Cookie",
format!("session={}; HttpOnly; SameSite=Strict; Path=/{}", token, self.cookie_suffix())
format!("session={}; HttpOnly; SameSite=Lax; Path=/{}", token, self.cookie_suffix())
.parse()
.unwrap(),
);
response.headers_mut().append(
"Set-Cookie",
format!("csrf_token={}; SameSite=Strict; Path=/{}", csrf_token, self.cookie_suffix())
format!("csrf_token={}; SameSite=Lax; Path=/{}", csrf_token, self.cookie_suffix())
.parse()
.unwrap(),
);
// Remember-me: HMAC-signed, survives backend restarts (30-day TTL)
response.headers_mut().append(
"Set-Cookie",
format!("remember={}; HttpOnly; SameSite=Strict; Path=/; Max-Age={}{}", remember_token, REMEMBER_TTL, self.cookie_suffix())
format!("remember={}; HttpOnly; SameSite=Lax; Path=/; Max-Age={}{}", remember_token, REMEMBER_TTL, self.cookie_suffix())
.parse()
.unwrap(),
);
@ -911,7 +917,7 @@ impl RpcHandler {
response.headers_mut().append(
"Set-Cookie",
format!(
"session={}; HttpOnly; SameSite=Strict; Path=/{}",
"session={}; HttpOnly; SameSite=Lax; Path=/{}",
new_token,
self.cookie_suffix()
)
@ -921,7 +927,7 @@ impl RpcHandler {
response.headers_mut().append(
"Set-Cookie",
format!(
"csrf_token={}; SameSite=Strict; Path=/{}",
"csrf_token={}; SameSite=Lax; Path=/{}",
csrf_token,
self.cookie_suffix()
)
@ -931,7 +937,7 @@ impl RpcHandler {
response.headers_mut().append(
"Set-Cookie",
format!(
"remember={}; HttpOnly; SameSite=Strict; Path=/; Max-Age={}{}",
"remember={}; HttpOnly; SameSite=Lax; Path=/; Max-Age={}{}",
remember_token,
REMEMBER_TTL,
self.cookie_suffix()
@ -958,7 +964,7 @@ impl RpcHandler {
response.headers_mut().append(
"Set-Cookie",
format!(
"session={}; HttpOnly; SameSite=Strict; Path=/{}",
"session={}; HttpOnly; SameSite=Lax; Path=/{}",
new_token,
self.cookie_suffix()
)
@ -968,7 +974,7 @@ impl RpcHandler {
response.headers_mut().append(
"Set-Cookie",
format!(
"csrf_token={}; SameSite=Strict; Path=/{}",
"csrf_token={}; SameSite=Lax; Path=/{}",
csrf_token,
self.cookie_suffix()
)
@ -986,13 +992,13 @@ impl RpcHandler {
let secure_suffix = if self.config.dev_mode { "" } else { "; Secure" };
response.headers_mut().append(
"Set-Cookie",
format!("session=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0{}", secure_suffix)
format!("session=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0{}", secure_suffix)
.parse()
.unwrap(),
);
response.headers_mut().append(
"Set-Cookie",
format!("csrf_token=; SameSite=Strict; Path=/; Max-Age=0{}", secure_suffix)
format!("csrf_token=; SameSite=Lax; Path=/; Max-Age=0{}", secure_suffix)
.parse()
.unwrap(),
);
@ -1003,13 +1009,13 @@ impl RpcHandler {
let suffix = self.cookie_suffix();
response.headers_mut().append(
"Set-Cookie",
format!("session={}; HttpOnly; SameSite=Strict; Path=/{}", new_session, suffix)
format!("session={}; HttpOnly; SameSite=Lax; Path=/{}", new_session, suffix)
.parse()
.unwrap(),
);
response.headers_mut().append(
"Set-Cookie",
format!("csrf_token={}; SameSite=Strict; Path=/{}", new_csrf, suffix)
format!("csrf_token={}; SameSite=Lax; Path=/{}", new_csrf, suffix)
.parse()
.unwrap(),
);