dioxus-cookie

Crates.iodioxus-cookie
lib.rsdioxus-cookie
version0.1.0
created_at2026-01-21 20:41:03.270208+00
updated_at2026-01-21 20:41:03.270208+00
descriptionUnified cookie storage for Dioxus fullstack apps that fills the gap in native platforms with keychain integration and encrypted file-vault fallback for simulators
homepage
repositoryhttps://github.com/rapporian/dioxus-cookie
max_upload_size
id2060073
size148,998
sebastian® (rapporian)

documentation

https://docs.rs/dioxus-cookie

README

dioxus-cookie

Unified cookie storage for Dioxus fullstack apps that fills the gap in native platforms. One interface for web, desktop, iOS, and Android—no platform-specific code, no #[cfg] blocks, no silent auth failures. Includes encrypted file-vault fallback for consistent simulator development.

The Problem

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/Android None Cookies silently lost
iOS Simulator Keychain blocked Entitlement errors

When your server function sets a session cookie, it works perfectly on web. But on desktop and mobile, that cookie vanishes into the void—your auth breaks with no error message.

The Solution

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
  • Android: Secure storage via keyring
  • iOS Simulator: Encrypted file fallback (debug builds only)

One API. All platforms. Your auth code stays clean.

Quick Start

1. Add to Cargo.toml:

[dependencies]
dioxus-cookie = { version = "0.1", 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, SameSite};
use std::time::Duration;

#[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.

Installation

Feature Flags

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.1", features = ["server", "desktop"] }

Fullstack app with mobile:

[dependencies]
dioxus-cookie = { version = "0.1", features = ["server", "desktop", "mobile"] }

iOS Simulator development (add mobile-sim for file-based fallback):

[dependencies]
dioxus-cookie = { version = "0.1", features = ["server", "mobile-sim"] }

How Features Map to Build Targets

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)

You enable all the features your project needs in Cargo.toml, then each dx build target uses the appropriate backend.

All Features

Feature Description
server Server-side cookie handling via HTTP headers
desktop Desktop platforms with system keyring storage
mobile iOS/Android with Keychain/Keystore storage
mobile-sim Mobile + file fallback for iOS Simulator development
file-store Encrypted file fallback (debug builds only, see Security)

Usage

Setting Cookies

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)
}

Reading Cookies

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();

Clearing Cookies

use dioxus_cookie as cookie;

#[server]
async fn logout() -> Result<(), ServerFnError> {
    cookie::clear("session")?;
    Ok(())
}

Security

Secure Defaults

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

HttpOnly Enforcement

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.

File Storage Fallback (file-store feature)

The file-store feature provides an encrypted file-based fallback for environments where the system keychain is unavailable:

Environment Why keychain fails
iOS Simulator Missing keychain entitlements
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 iOS Simulator development, or add file-store directly for other fallback scenarios.

Architecture

┌─────────────────────────────────────────────────────┐
│                dioxus-cookie API                    │
│            get / set / clear / list_names           │
└────────────────────────┬────────────────────────────┘
                         │
      ┌──────────────────┼──────────────────┐
      ▼                  ▼                  ▼
┌───────────┐      ┌───────────┐      ┌───────────┐
│  server   │      │  native   │      │   stubs   │
│  module   │      │  module   │      │  module   │
└─────┬─────┘      └─────┬─────┘      └─────┬─────┘
      │                  │                  │
      ▼                  ▼                  ▼
┌───────────┐      ┌───────────┐      ┌───────────┐
│ HTTP      │      │ System    │      │ document  │
│ Headers   │      │ Keyring   │      │ .cookie   │
└───────────┘      └───────────┘      └───────────┘

API Reference

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 all accessible cookie names.
get_storage_type() Get the active storage backend.

Troubleshooting

"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.

"GLOBAL_REQUEST_CLIENT already initialized" Call cookie::init() before dioxus::launch().

get() returns None for a cookie that was set The cookie is HttpOnly. This is intentional—use get_internal() only for session restoration.

License

MIT

Commit count: 1

cargo fmt