fix(auth): resolve CSP blocking and session refresh loop
Fix two issues causing login loop after successful admin setup: 1. CSP blocking inline styles: The frontend JavaScript dynamically sets inline styles (e.g., element.style.display = 'none') for UI state management. The CSP header only allowed 'self' for style-src, blocking these dynamic styles. Added 'unsafe-inline' to style-src directive. 2. Session refresh 401 errors: The cookie Secure flag defaulted to true when OXICLOUD_BASE_URL was not set, causing cookies to not be sent over HTTP in Docker deployments. Changed the default to false when the base URL is not explicitly set to HTTPS, with clear logging to guide users to set OXICLOUD_COOKIE_SECURE=true for production. Fixes #203
This commit is contained in:
@@ -30,8 +30,10 @@ pub const CSRF_HEADER: &str = "x-csrf-token";
|
||||
/// Resolution order:
|
||||
/// 1. `OXICLOUD_COOKIE_SECURE=true|false` — explicit override.
|
||||
/// 2. `OXICLOUD_BASE_URL` starts with `https` → `true`.
|
||||
/// 3. **Default: `true`** (safe-by-default). Set `OXICLOUD_COOKIE_SECURE=false`
|
||||
/// explicitly for plain-HTTP development environments.
|
||||
/// 3. `OXICLOUD_BASE_URL` starts with `http` → `false`.
|
||||
/// 4. **Default: `false`** for compatibility with HTTP deployments
|
||||
/// (Docker, local development). Set `OXICLOUD_COOKIE_SECURE=true`
|
||||
/// explicitly for production HTTPS environments.
|
||||
fn cookie_secure() -> bool {
|
||||
if let Ok(v) = std::env::var("OXICLOUD_COOKIE_SECURE") {
|
||||
let secure = v == "true" || v == "1";
|
||||
@@ -44,18 +46,25 @@ fn cookie_secure() -> bool {
|
||||
}
|
||||
return secure;
|
||||
}
|
||||
// Auto-detect from base URL, defaulting to secure when unset
|
||||
let secure = std::env::var("OXICLOUD_BASE_URL")
|
||||
.map(|u| u.starts_with("https"))
|
||||
.unwrap_or(true);
|
||||
if !secure {
|
||||
tracing::warn!(
|
||||
"OXICLOUD_BASE_URL does not start with https — \
|
||||
cookie Secure flag is OFF. Set OXICLOUD_COOKIE_SECURE=true \
|
||||
to override if your proxy terminates TLS."
|
||||
);
|
||||
// Auto-detect from base URL, defaulting to insecure for compatibility
|
||||
match std::env::var("OXICLOUD_BASE_URL") {
|
||||
Ok(url) if url.starts_with("https") => true,
|
||||
Ok(url) if url.starts_with("http://") => {
|
||||
tracing::info!(
|
||||
"OXICLOUD_BASE_URL is HTTP — cookie Secure flag is OFF. \
|
||||
Set OXICLOUD_COOKIE_SECURE=true to override if your proxy terminates TLS."
|
||||
);
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
// Default to false for compatibility with HTTP deployments
|
||||
tracing::info!(
|
||||
"OXICLOUD_BASE_URL not set — defaulting to non-secure cookies \
|
||||
for HTTP compatibility. Set OXICLOUD_COOKIE_SECURE=true for HTTPS deployments."
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
secure
|
||||
}
|
||||
|
||||
/// Build a `Set-Cookie` header value.
|
||||
|
||||
+6
-3
@@ -416,15 +416,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
app = app
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("content-security-policy"),
|
||||
// All inline scripts and styles have been migrated to external
|
||||
// files, so 'unsafe-inline' is no longer needed.
|
||||
// Note: 'unsafe-inline' is required for style-src because the
|
||||
// frontend JavaScript dynamically sets inline styles (e.g.,
|
||||
// element.style.display = 'none'). This is a common pattern
|
||||
// for UI state management and cannot be easily migrated to
|
||||
// external CSS classes without significant refactoring.
|
||||
// frame-src: '*' only matches network schemes, so 'blob:' must be
|
||||
// listed explicitly for inline PDF/document viewers.
|
||||
// media-src: needed for blob: video/audio playback.
|
||||
HeaderValue::from_static(
|
||||
"default-src 'self'; \
|
||||
script-src 'self'; \
|
||||
style-src 'self'; \
|
||||
style-src 'self' 'unsafe-inline'; \
|
||||
img-src 'self' data: blob:; \
|
||||
media-src 'self' blob:; \
|
||||
connect-src 'self'; \
|
||||
|
||||
Reference in New Issue
Block a user