DEV Community

tengxgfyrz67s
tengxgfyrz67s

Posted on

Cookie and Session Management in Hyperlane

Project Code:https://github.com/hyperlane-dev/hyperlane

Introduction

Cookie and session management are fundamental aspects of modern web development. They enable stateful interactions in the inherently stateless HTTP protocol, allowing servers to remember users across multiple requests. In the hyperlane framework, cookie handling is built directly into the request and response APIs, providing a clean and efficient way to manage user sessions.

This article covers everything you need to know about working with cookies in hyperlane — from reading incoming cookies to creating and sending new ones using the powerful CookieBuilder API.

Reading Cookies from Requests

Getting All Cookies

Hyperlane makes it easy to retrieve all cookies from an incoming HTTP request:

let cookies = ctx.get_request().try_get_cookies();
Enter fullscreen mode Exit fullscreen mode

This returns all cookies sent by the client as part of the request. The try_get_cookies method parses the Cookie header and returns the cookie collection.

Getting a Specific Cookie

When you need to access a particular cookie by name, use:

let cookie = ctx.get_request().try_get_cookie("session_id");
Enter fullscreen mode Exit fullscreen mode

This returns an Option type — Some(cookie_value) if the cookie exists, or None if it doesn't. This pattern encourages proper error handling and avoids panics from missing cookies.

Creating Cookies with CookieBuilder

Hyperlane provides a fluent CookieBuilder API for creating cookies with various attributes. The builder pattern allows you to chain configuration calls for a clean, readable syntax.

Basic Cookie Creation

let cookie = CookieBuilder::new("session_id", "abc123")
    .set_path("/")
    .http_only()
    .build();
Enter fullscreen mode Exit fullscreen mode

This creates a cookie named session_id with the value abc123, scoped to the root path and marked as HttpOnly (inaccessible to JavaScript, providing XSS protection).

Full Cookie Configuration

The CookieBuilder supports all standard cookie attributes:

let cookie = CookieBuilder::new("session", "token123")
    .set_expires("Wed, 21 Oct 2025 07:28:00 GMT")
    .set_domain("example.com")
    .set_same_site("Strict")
    .set_max_age(3600)
    .set_path("/")
    .secure()
    .http_only()
    .build();
Enter fullscreen mode Exit fullscreen mode

Let's break down each attribute:

  • set_expires — Sets the cookie's expiration date in HTTP date format. After this date, the browser will no longer send the cookie.
  • set_domain — Specifies which domain the cookie is valid for. The cookie will only be sent to the specified domain and its subdomains.
  • set_same_site — Controls cross-site request behavior. "Strict" prevents the cookie from being sent in cross-site requests, while "Lax" allows it for top-level navigations.
  • set_max_age — Sets the cookie's lifetime in seconds. After this duration, the cookie expires. This is preferred over set_expires as it is relative to the current time.
  • set_path — Restricts the cookie to a specific URL path. The cookie is only sent for requests to paths under this prefix.
  • secure() — Marks the cookie as secure, meaning it will only be sent over HTTPS connections.
  • http_only() — Marks the cookie as HttpOnly, preventing access from JavaScript to mitigate XSS attacks.

Clearing Cookies

To delete a cookie from the client, create a new cookie with the same name but with an empty value and max_age of 0:

let clear_cookie = CookieBuilder::new("session", "")
    .set_max_age(0)
    .build();
Enter fullscreen mode Exit fullscreen mode

This tells the browser to immediately expire the cookie, effectively removing it.

Sending Cookies in Responses

Once you have built a cookie, you attach it to the response using the Set-Cookie header:

let cookie = CookieBuilder::new("session_id", "abc123")
    .set_path("/")
    .http_only()
    .build();

ctx.get_mut_response().set_header(SET_COOKIE, &cookie);
Enter fullscreen mode Exit fullscreen mode

The SET_COOKIE constant is the standard header name for setting cookies. If you need to set multiple cookies, use add_header instead of set_header to append additional Set-Cookie headers:

ctx.get_mut_response().add_header(SET_COOKIE, &cookie1);
ctx.get_mut_response().add_header(SET_COOKIE, &cookie2);
Enter fullscreen mode Exit fullscreen mode

Complete Session Management Example

Here is a complete example showing a login handler that creates a session cookie and a logout handler that clears it:

#[route("/login")]
struct LoginHandler;

impl ServerHook for LoginHandler {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        // Verify credentials (simplified for demonstration)
        let body = ctx.get_request().get_body_string();

        // Create a session cookie on successful authentication
        let session_cookie = CookieBuilder::new("session_id", "abc123")
            .set_path("/")
            .http_only()
            .secure()
            .build();

        ctx.get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(200)
            .set_header(SET_COOKIE, &session_cookie)
            .set_body("Login successful");

        let data = ctx.get_mut_response().build();
        stream.try_send(data).await;

        Status::Continue
    }
}

#[route("/logout")]
struct LogoutHandler;

impl ServerHook for LogoutHandler {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        // Clear the session cookie
        let clear_cookie = CookieBuilder::new("session_id", "")
            .set_max_age(0)
            .build();

        ctx.get_mut_response()
            .set_version(HttpVersion::Http1_1)
            .set_status_code(200)
            .set_header(SET_COOKIE, &clear_cookie)
            .set_body("Logged out successfully");

        let data = ctx.get_mut_response().build();
        stream.try_send(data).await;

        Status::Continue
    }
}
Enter fullscreen mode Exit fullscreen mode

Cookie-Based Authentication Middleware

You can combine cookie reading with authentication logic to create middleware that protects routes:

struct AuthMiddleware;

impl ServerHook for AuthMiddleware {
    async fn new(_: &mut Stream, ctx: &mut Context) -> Self {
        Self
    }

    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let session_cookie = ctx.get_request().try_get_cookie("session_id");

        match session_cookie {
            Some(session_id) if !session_id.is_empty() => {
                // Session exists — proceed to the next handler
                Status::Continue
            }
            _ => {
                // No valid session — reject the request
                let data = ctx.get_mut_response()
                    .set_status_code(401)
                    .set_body("Unauthorized")
                    .build();

                if stream.try_send(data).await.is_err() {
                    stream.set_closed(true);
                }

                Status::Reject
            }
        }
    }
}

server.request_middleware::<AuthMiddleware>();
Enter fullscreen mode Exit fullscreen mode

Session Management Best Practices

  1. Always use HttpOnly for session cookies. This prevents JavaScript access and mitigates XSS-based session theft.

  2. Use Secure flag in production. Session cookies should only be transmitted over HTTPS to prevent interception.

  3. Set SameSite attribute. Use "Strict" or "Lax" to protect against CSRF attacks. "Strict" provides the strongest protection but may affect user experience.

  4. Use Max-Age instead of Expires. Max-Age is relative to the current time and avoids clock synchronization issues between server and client.

  5. Keep session identifiers unpredictable. Use cryptographically random values for session IDs rather than sequential or predictable values.

  6. Implement proper session expiration. Set reasonable Max-Age values and always clear cookies on logout by setting Max-Age to 0.

  7. Scope cookies appropriately. Use set_path to limit cookie transmission to only the paths that need them.

Combining Cookies with Other Hyperlane Features

Cookies and Attributes

You can use hyperlane's attribute system to store session data that persists across multiple requests on the same connection:

ctx.set_attribute("user_id", "123");
let user_id: Option<String> = ctx.try_get_attribute("user_id");
Enter fullscreen mode Exit fullscreen mode

Cookies and SSE

When working with Server-Sent Events, cookies set in the initial response are sent as part of the response headers:

let session_cookie = CookieBuilder::new("session_id", "abc123")
    .set_path("/")
    .http_only()
    .build();

let data = ctx.get_mut_response()
    .set_header(CONTENT_TYPE, TEXT_EVENT_STREAM)
    .set_header(SET_COOKIE, &session_cookie)
    .set_body(Vec::new())
    .build();

stream.try_send(data).await;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Hyperlane provides a comprehensive and ergonomic API for cookie and session management. The CookieBuilder fluent interface makes it easy to create cookies with all standard attributes, while the request API provides safe methods for reading incoming cookies. By following best practices — using HttpOnly, Secure, and SameSite attributes — you can build secure session management systems that protect your users from common web vulnerabilities.

Whether you are building a simple login system or a complex multi-tenant application, hyperlane's cookie management tools give you everything you need to maintain stateful interactions in your web applications.


Project Code:https://github.com/hyperlane-dev/hyperlane

Top comments (0)