# How to Use Signed and Encrypted Cookies in Topcoat

> Learn to use signed and encrypted cookies in Topcoat with the cookie! macro and SecretKey. Protect your cookie data effectively and enhance application security.

- Repository: [Tokio/topcoat](https://github.com/tokio-rs/topcoat)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Use the `cookie!` macro with the `signed` or `private` attributes to protect cookie data, and register a `SecretKey` in the application context to enable HMAC signatures or AES-GCM encryption.**

The tokio-rs/topcoat framework provides a unified API for handling **signed and encrypted cookies in Topcoat** through the declarative `cookie!` macro and the underlying `CookieJar` abstraction. By leveraging application context injection, you can enforce data integrity and confidentiality without implementing cryptographic primitives manually.

## Understanding Cookie Protection Mechanisms

Topcoat implements two distinct protection strategies in [`crates/topcoat/src/cookie.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/cookie.rs), each designed for specific security requirements.

### Signed Cookies (Integrity Protection)

**Signed cookies** store the plaintext value on the client while appending an HMAC-based cryptographic signature. The `SignedCookie` type handles this verification automatically, detecting any tampering attempts when the cookie is read. According to the Topcoat source code, the signature is verified on each request using the secret key retrieved from `app_context::<SecretKey>(cx)`.

### Private Cookies (Confidentiality)

**Private (encrypted) cookies** ensure confidentiality by encrypting the value before transmission. The `PrivateCookie` struct in the same file uses authenticated encryption (AES-GCM) to prevent both disclosure and modification. The macro adds the `private` attribute to trigger this encryption mode, storing only the ciphertext on the client.

## Configuring the Secret Key

Both mechanisms share the same 256-bit key material, which you must register in the application context at startup. This key is typically loaded from environment variables or secure configuration stores.

```rust
use topcoat::prelude::*;

#[derive(Clone)]
struct SecretKey(Vec<u8>);

fn main() {
    topcoat::run(|mut app| {
        let key = std::env::var("TOPCOAT_COOKIE_KEY")
            .expect("TOPCOAT_COOKIE_KEY must be set")
            .into_bytes();
        
        // Register the key in the app context
        app.app_context(SecretKey(key));
    });
}

```

## Setting Signed and Encrypted Cookies

Use the `cookie!` macro declaratively to specify the protection level. Topcoat automatically signs or encrypts the value and sets the appropriate response headers.

**Setting a signed cookie:**

```rust
#[topcoat::get("/set-signed")]
async fn set_signed(cx: &Cx) -> impl IntoResponse {
    // The `signed` attribute triggers HMAC signing
    let signed = cookie!("session_id", "abc123", signed);
    cookies(cx).add(signed);
    "Signed cookie set"
}

```

**Setting an encrypted (private) cookie:**

```rust
#[topcoat::get("/set-private")]
async fn set_private(cx: &Cx) -> impl IntoResponse {
    // The `private` attribute triggers AES-GCM encryption
    let private = cookie!("auth_token", "secret-token", private);
    cookies(cx).add(private);
    "Private cookie set"
}

```

## Reading and Verifying Cookies

Access the `CookieJar` via `cookies(cx)` to read values. Topcoat transparently verifies signatures or decrypts values before returning them to your handler, returning `None` if verification fails or if the cookie is missing.

```rust
#[topcoat::get("/read")]
async fn read_cookie(cx: &Cx) -> impl IntoResponse {
    match cookies(cx).get("session_id") {
        Some(value) => format!("Verified value: {}", value),
        None => "No valid cookie found".into()
    }
}

```

## Deleting Cookies

Remove cookies by calling the `remove` method on the `CookieJar` instance:

```rust
#[topcoat::get("/logout")]
async fn logout(cx: &Cx) -> impl IntoResponse {
    cookies(cx).remove("session_id");
    cookies(cx).remove("auth_token");
    "Cookies cleared"
}

```

## Key Source Files

The cookie implementation spans the following locations in the tokio-rs/topcoat repository:

- **[`crates/topcoat/src/cookie.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/cookie.rs)** – Contains the `SignedCookie` and `PrivateCookie` implementations, along with the `CookieJar` abstraction.
- **[`crates/topcoat/docs/cookie.md`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/docs/cookie.md)** – User-facing documentation covering macro semantics, SameSite flags, and expiration handling.
- **[`crates/topcoat/src/lib.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/lib.rs)** – Re-exports the public cookie API through the façade crate.

## Summary

- **Register a secret key** using `app.app_context(SecretKey(key))` at application startup to enable cryptographic operations.
- **Use `cookie!(..., signed)`** for integrity protection (HMAC) and **`cookie!(..., private)`** for confidentiality (AES-GCM).
- **Access cookies** via `cookies(cx).get("name")`, which automatically verifies signatures or decrypts values using the context-registered key.
- **Reference implementation** resides in [`crates/topcoat/src/cookie.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/cookie.rs), with additional documentation available in [`crates/topcoat/docs/cookie.md`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/docs/cookie.md).

## Frequently Asked Questions

### What is the difference between signed and private cookies in Topcoat?

**Signed cookies** ensure data integrity by appending an HMAC signature to the plaintext value, preventing tampering but allowing the client to read the content. **Private cookies** encrypt the entire value using AES-GCM, ensuring confidentiality so the client cannot read the data, while also providing integrity protection.

### How do I rotate the secret key without invalidating existing sessions?

The current implementation in [`crates/topcoat/src/cookie.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/cookie.rs) uses a single key from the application context. To rotate keys, you would typically deploy the new key while accepting the old key for verification during a transition period, though the source code suggests using the context-retrieved key directly via `app_context::<SecretKey>(cx)`.

### Does Topcoat use separate keys for signing and encryption?

No. As implemented in the source code, both **signed** and **private** cookie modes share the same 256-bit key material supplied via the application context. The `SignedCookie` and `PrivateCookie` types derive their cryptographic material from the same `SecretKey` registration.

### What happens if a client tampers with a signed cookie?

When you call `cookies(cx).get("name")` on a tampered signed cookie, Topcoat detects the signature mismatch during HMAC verification in [`crates/topcoat/src/cookie.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat/src/cookie.rs) and returns `None` to your handler, effectively treating the cookie as if it were never set.