fix: session_expired after login on HTTP deployments (#241)

Three changes to fix the immediate-logout issue reported by multiple
Docker users:

1. Add explicit `credentials: 'same-origin'` to the login fetch call.
   This was the only fetch in the entire codebase missing it. While
   modern browsers default to 'same-origin', some privacy configs or
   older engines may default to 'omit', silently dropping Set-Cookie
   headers from the login response.

2. Post-login cookie verification: after a successful login, the
   frontend now checks that the CSRF cookie (non-HttpOnly, readable
   by JS) was actually stored before redirecting. If the browser
   rejected the cookies, a clear error message is shown explaining
   the OXICLOUD_COOKIE_SECURE / HTTP mismatch.

3. Server-side diagnostic: the login handler now warns in logs when
   Secure cookies are set on a request that didn't arrive via HTTPS
   (no X-Forwarded-Proto: https header), pointing admins to the
   OXICLOUD_COOKIE_SECURE=false fix.

Root cause: users who set OXICLOUD_BASE_URL=https://... (or have
OXICLOUD_COOKIE_SECURE=true) but access via plain HTTP get cookies
with the Secure flag, which browsers silently reject over HTTP.
This commit is contained in:
Diocrafts
2026-04-12 01:38:19 +02:00
parent 5be035a172
commit c512534bfa
3 changed files with 40 additions and 1 deletions
+4
View File
@@ -34,6 +34,10 @@ pub const CSRF_HEADER: &str = "x-csrf-token";
/// 4. **Default: `false`** for compatibility with HTTP deployments
/// (Docker, local development). Set `OXICLOUD_COOKIE_SECURE=true`
/// explicitly for production HTTPS environments.
pub fn is_cookie_secure() -> bool {
cookie_secure()
}
fn cookie_secure() -> bool {
if let Ok(v) = std::env::var("OXICLOUD_COOKIE_SECURE") {
let secure = v == "true" || v == "1";
@@ -121,6 +121,7 @@ async fn register(
async fn login(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Json(dto): Json<LoginDto>,
) -> Result<Response, AppError> {
// Add detailed logging for debugging
@@ -204,6 +205,25 @@ async fn login(
state.core.config.auth.refresh_token_expiry_secs,
);
cookie_auth::append_csrf_cookie(response.headers_mut(), auth_response.expires_in);
// Diagnostic: warn when Secure cookies are set but the request
// arrived over plain HTTP — the browser will reject them (#241).
if cookie_auth::is_cookie_secure() {
let is_tls = headers
.get("x-forwarded-proto")
.and_then(|v| v.to_str().ok())
.is_some_and(|p| p.eq_ignore_ascii_case("https"));
if !is_tls {
tracing::warn!(
"Login for '{}': Secure cookies are enabled but the request \
does not appear to be over HTTPS (no X-Forwarded-Proto: https). \
The browser may reject the cookies. Set OXICLOUD_COOKIE_SECURE=false \
in .env if you access OxiCloud via plain HTTP.",
dto.username,
);
}
}
Ok(response)
}
Err(err) => {