Unified cookie storage for Dioxus fullstack apps. Cookies set on the server—including secure HttpOnly session tokens—work on web but silently vanish on desktop and mobile. dioxus-cookie fixes that. One interface for all platforms, no #[cfg] blocks. Includes encrypted file-vault fallback for simulator/emulator development.
Dioxus fullstack apps compile to multiple targets from one codebase, but cookie handling is fragmented:
| Platform | Cookie Mechanism | What Happens |
|---|---|---|
| Web (server) | HTTP Set-Cookie headers |
Works |
| Web (WASM) | document.cookie |
Works |
| Desktop | None | Cookies silently lost |
| iOS | None | Cookies silently lost |
| Android | None | Cookies silently lost |
| iOS Simulator | Keychain blocked | Entitlement errors |
| Android Emulator | KeyStore may fail | Device-dependent |
When your server function sets a cookie, it works perfectly on web. But on desktop and mobile, that cookie vanishes—whether it's a session token, user preference, or any other state.
dioxus-cookie bridges this gap with platform-appropriate storage:
- Server: HTTP headers (standard web cookies)
- Browser:
document.cookie(standard web cookies) - Desktop: System keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service)
- iOS: Keychain (via keyring crate)
- Android: KeyStore (experimental, via android-keyring crate)
- Simulators/Emulators: Encrypted file fallback (debug builds only)
One API. Your cookie code stays clean—auth, preferences, everything.
1. Add to Cargo.toml:
[dependencies]
dioxus-cookie = { version = "0.2", features = ["server", "desktop"] }2. Initialize before launch:
use dioxus_cookie as cookie;
fn main() {
cookie::init();
dioxus::launch(App);
}3. Use in server functions:
use dioxus_cookie::{self as cookie, CookieOptions};
#[server]
async fn login(credentials: Credentials) -> Result<User, ServerFnError> {
let user = authenticate(credentials).await?;
cookie::set("session", &user.token, &CookieOptions::default())?;
Ok(user)
}
#[server]
async fn logout() -> Result<(), ServerFnError> {
cookie::clear("session")?;
Ok(())
}That's it. Cookies now work on web, desktop, iOS, and Android.
Enable features for all platforms your app targets. The correct backend is selected automatically at compile time based on the build target.
Typical fullstack app (web + desktop):
[dependencies]
dioxus-cookie = { version = "0.2", features = ["server", "desktop"] }Fullstack app with iOS/Android:
[dependencies]
dioxus-cookie = { version = "0.2", features = ["server", "desktop", "mobile"] }Simulator/Emulator development (add mobile-sim for file-based fallback):
[dependencies]
dioxus-cookie = { version = "0.2", features = ["server", "mobile-sim"] }When you run dx build or dx serve, the correct backend is selected automatically based on the build target:
| Build command | Backend used |
|---|---|
dx serve / dx build --platform web (server) |
HTTP headers (server feature) |
dx build --platform web (WASM client) |
document.cookie (no feature needed) |
dx build --platform desktop |
System keyring (desktop feature) |
dx build --platform ios |
iOS Keychain (mobile feature) |
dx build --platform android |
Android KeyStore (mobile feature, experimental) |
You enable all the features your project needs in Cargo.toml, then each dx build target uses the appropriate backend.
| Feature | Description |
|---|---|
server |
Server-side cookie handling via HTTP headers |
desktop |
Desktop platforms with system keyring storage |
mobile |
iOS/Android with Keychain/KeyStore storage (Android is experimental) |
mobile-sim |
Mobile + file fallback for simulator/emulator development |
file-store |
Encrypted file fallback (debug builds only, see Security) |
android-file |
Force encrypted file storage on Android (skip KeyStore for maximum reliability) |
Android support uses the experimental android-keyring crate to access Android's KeyStore. While KeyStore provides the most secure storage, there are important reliability considerations.
| Scenario | Issue |
|---|---|
| Emulators | Some images have incomplete KeyStore implementations |
| Older devices | KeyStore behavior varies by Android version and manufacturer |
| CI/CD | Running without full device context |
| NDK issues | JNI bridge may fail to initialize |
| Features | Storage Method | Security | Reliability | Recommended For |
|---|---|---|---|---|
mobile |
Android KeyStore | Highest (hardware-backed) | Variable | Production on tested devices |
mobile + file-store |
KeyStore with file fallback | High | Good | Production with safety net |
mobile + android-file |
Encrypted file only | Medium | Highest | Maximum compatibility |
mobile-sim |
KeyStore with file fallback | High | Good | Development/testing |
For development and emulator testing:
dioxus-cookie = { version = "0.2", features = ["server", "mobile-sim"] }For production (reliability-focused):
dioxus-cookie = { version = "0.2", features = ["server", "mobile", "android-file"] }Skips KeyStore entirely. Slightly less secure but works on all devices.
For production (security-focused, tested devices only):
dioxus-cookie = { version = "0.2", features = ["server", "mobile"] }Uses KeyStore only. Panics if KeyStore unavailable—only use after testing on target devices.
| Storage | Protection | Vulnerability |
|---|---|---|
| Android KeyStore | Hardware-backed on supported devices | None (keys cannot be extracted) |
Encrypted File (android-file) |
AES-256-GCM + app sandbox | Root access can extract data |
Note: File storage is protected by Android's app sandbox (other apps cannot access) and the filesystem is encrypted on Android 10+. The main risk is on rooted devices where an attacker has filesystem access.
| Features | KeyStore Works | Result |
|---|---|---|
mobile |
Yes | Uses KeyStore |
mobile |
No | Panics |
mobile + file-store |
Yes | Uses KeyStore |
mobile + file-store |
No | Falls back to file |
mobile + android-file |
N/A | Skips KeyStore, uses file |
mobile-sim |
Yes | Uses KeyStore |
mobile-sim |
No | Falls back to file |
fn main() {
dioxus_cookie::init();
let storage = dioxus_cookie::get_storage_type();
println!("Cookie storage: {storage}");
// Android values: "android-keystore" or "android-file"
dioxus::launch(App);
}use dioxus_cookie::{self as cookie, CookieOptions, SameSite};
use std::time::Duration;
#[server]
async fn login(credentials: Credentials) -> Result<User, ServerFnError> {
let user = authenticate(credentials).await?;
// Simple: use secure defaults
cookie::set("session", &user.token, &CookieOptions::default())?;
// Or customize options
cookie::set("session", &user.token, &CookieOptions {
max_age: Some(Duration::from_secs(86400 * 7)), // 7 days
http_only: true,
secure: true,
same_site: SameSite::Strict,
path: "/".to_string(),
})?;
Ok(user)
}use dioxus_cookie as cookie;
// Returns None for HttpOnly cookies (by design)
let preference = cookie::get("theme");
// List all accessible cookie names
let names = cookie::list_names();use dioxus_cookie as cookie;
#[server]
async fn logout() -> Result<(), ServerFnError> {
cookie::clear("session")?;
Ok(())
}CookieOptions::default() prioritizes security:
| Option | Default | Purpose |
|---|---|---|
http_only |
true |
Prevents JavaScript access |
secure |
true |
HTTPS only |
same_site |
Lax |
CSRF protection |
path |
"/" |
Available to all routes |
On native platforms, dioxus-cookie enforces HttpOnly the same way browsers do:
use dioxus_cookie as cookie;
cookie::get("session") // → None (HttpOnly blocked)
cookie::get_internal("session") // → Some(...) (internal use only)HttpOnly cookies are still sent automatically with HTTP requests.
The file-store feature provides an encrypted file-based fallback for environments where the system keychain is unavailable:
| Environment | Why keychain/keystore fails |
|---|---|
| iOS Simulator | Missing keychain entitlements |
| Android Emulator | KeyStore may be unavailable |
| Linux without D-Bus | No Secret Service available |
| CI/CD pipelines | No desktop session |
| Docker containers | No keychain access |
Security limitations:
- DEBUG BUILDS ONLY — Automatically disabled in release builds
- Obfuscation, not protection — AES-256-GCM encryption with a machine-derived key deters casual inspection but does not protect against local attackers with file access
- Not for production — Production apps must use real keychain storage
Use mobile-sim (which includes file-store) for simulator/emulator development, or add file-store directly for other fallback scenarios.
┌─────────────────────────────────────────────────────┐
│ dioxus-cookie API │
│ get / set / clear / list_names │
└────────────────────────┬────────────────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ server │ │ native │ │ wasm │
│ module │ │ module │ │ module │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ HTTP │ │ System │ │ document │
│ Headers │ │ Keyring │ │ .cookie │
└───────────┘ └───────────┘ └───────────┘
| Function | Description |
|---|---|
init() |
Initialize the cookie system. Call before dioxus::launch(). |
get(name) |
Get a cookie value. Returns None for HttpOnly cookies. |
get_internal(name) |
Get a cookie bypassing HttpOnly (internal use). |
set(name, value, options) |
Set a cookie with the given options. |
clear(name) |
Delete a cookie. |
list_names() |
List accessible cookie names (excludes HttpOnly on native). |
get_storage_type() |
Get the active storage backend. |
"No server context available"
Cookie operations that set HTTP headers must run inside #[server] functions.
Cookies not persisting on iOS Simulator
Use the mobile-sim feature. The simulator lacks keychain entitlements, so cookies fall back to encrypted file storage.
Cookies not persisting on Android
- Ensure
mobilefeature is enabled - The app must have proper NDK context (Dioxus Mobile provides this automatically)
- Consider enabling
file-storeas fallback for emulators or devices where KeyStore fails - Note: Android support is experimental (via android-keyring crate)
get() returns None for a cookie that was set
The cookie is HttpOnly. This is intentional—use get_internal() only for session restoration.
MIT