Cookies and Sessions

Finch provides two mechanisms for persisting data between HTTP requests: cookies (client-side) and sessions (server-side). Understanding the difference helps you choose the right tool:

  • Cookies are stored in the browser and sent with every request. They are suitable for lightweight user preferences (theme, language) that can be safely stored client-side.
  • Sessions are stored on the server. Only a session ID is sent to the browser. They are suitable for sensitive data such as the currently logged-in user.

Both are accessible through the rq object inside any controller or route handler.

Cookies

Cookies are key-value string pairs stored in the browser. Finch reads them from incoming Cookie headers and writes them via Set-Cookie response headers.

Use rq.getCookie(key, def: defaultValue) to read a cookie. The def parameter specifies what to return if the cookie does not exist:

// Plain cookie — value is stored as-is in the browser
var theme = rq.getCookie('theme', def: 'light');

// Encrypted cookie — value is decrypted server-side before returning
var token = rq.getCookie('auth_token', def: '', safe: true);

Use rq.addCookie(key, value) to set a cookie. The cookie is attached to the response:

// Plain cookie
rq.addCookie('theme', 'dark');

// Encrypted cookie — value is encrypted before sending to the browser
rq.addCookie('auth_token', 'abc123', safe: true);

To remove a cookie, Finch sets it with an expired max-age:

rq.removeCookie('theme');

Complete Example

class CookieController extends Controller {
  Future<String> showCookie() async {
    var value = rq.getCookie('test', def: 'not set');
    rq.addParam('cookieValue', value);
    return rq.renderView(path: 'example/cookie');
  }

  Future<String> addCookie() async {
    var key   = rq.get<String>('key', def: '');
    var value = rq.get<String>('value', def: '');
    var safe  = rq.get<bool>('safe', def: false);

    if (key.isNotEmpty) {
      rq.addCookie(key, value, safe: safe);
    }
    return rq.redirect('/example/cookie');
  }
}

Sessions

Sessions are managed by Dart's built-in HttpSession. Each visitor receives a unique session ID stored in a browser cookie, but the actual data lives on the server in memory.

Important: The default Dart HttpSession is memory-backed. Session data is lost when the server restarts. For production use, consider a persistent session store or store critical data in the database instead.

Read a Session Value

Use rq.getSession(key, def: defaultValue) to read a value. Returns def if the key does not exist:

var userEmail = rq.getSession('user', def: '');

Write a Session Value

Use rq.addSession(key, value) to store a value in the current session:

rq.addSession('user', '[email protected]');

Remove a Session Value

rq.session.remove('user');

Complete Example

A typical login/logout/dashboard flow using sessions:

class SessionController extends Controller {
  Future<String> login() async {
    var email    = rq.get<String>('email', def: '');
    var password = rq.get<String>('password', def: '');

    if (isValidUser(email, password)) {
      // Store the user's email in the session after successful authentication
      rq.addSession('user', email);
      return rq.redirect('/dashboard');
    }
    return rq.renderError(401);
  }

  Future<String> logout() async {
    // Remove the user from the session to log them out
    rq.session.remove('user');
    return rq.redirect('/login');
  }

  Future<String> dashboard() async {
    var email = rq.getSession('user', def: '');

    // If no session, the user is not logged in — redirect to login
    if (email.isEmpty) return rq.redirect('/login');

    rq.addParam('email', email);
    return rq.renderView(path: 'pages/dashboard');
  }
}

Encrypted Cookies

When safe: true is used, Finch encrypts the cookie value before sending it to the browser and decrypts it when reading. This prevents the user from reading or tampering with the stored value.

The encryption key is taken from FinchConfigs.cookiePassword:

FinchConfigs configs = FinchConfigs(
  cookiePassword: env['COOKIE_SECRET'] ?? 'change-this-in-production',
);
// Writing — stored encrypted in browser
rq.addCookie('prefs', '{"theme":"dark"}', safe: true);

// Reading back — Finch decrypts it automatically
var prefs = rq.getCookie('prefs', def: '{}', safe: true);

Security: Use a long, random cookiePassword. Store it as an environment variable. Never hardcode it or commit it to version control.

Reading Cookies in Templates

You can also read a cookie value directly inside a Jinja template:

{{ $e.getCookie('theme', 'light') }}

Cookies

// Plain cookie
var theme = rq.getCookie('theme', def: 'light');

// Encrypted cookie (decrypted using FinchConfigs.cookiePassword)
var token = rq.getCookie('auth_token', def: '', safe: true);
// Plain cookie
rq.addCookie('theme', 'dark');

// Encrypted cookie
rq.addCookie('auth_token', 'abc123', safe: true);
rq.removeCookie('theme');

Complete Example

class CookieController extends Controller {
  Future<String> showCookie() async {
    var value = rq.getCookie('test', def: 'not set');
    rq.addParam('cookieValue', value);
    return rq.renderView(path: 'example/cookie');
  }

  Future<String> addCookie() async {
    var key   = rq.get<String>('key', def: '');
    var value = rq.get<String>('value', def: '');
    var safe  = rq.get<bool>('safe', def: false);

    if (key.isNotEmpty) {
      rq.addCookie(key, value, safe: safe);
    }
    return rq.redirect('/example/cookie');
  }
}

Sessions

Sessions are server-side and stored in Dart's built-in HttpSession (memory-backed by default).

Read a Session Value

var userEmail = rq.getSession('user', def: '');

Write a Session Value

rq.addSession('user', '[email protected]');

Remove a Session Value

rq.session.remove('user');

Complete Example

class SessionController extends Controller {
  Future<String> login() async {
    var email    = rq.get<String>('email', def: '');
    var password = rq.get<String>('password', def: '');

    if (isValidUser(email, password)) {
      rq.addSession('user', email);
      return rq.redirect('/dashboard');
    }
    return rq.renderError(401);
  }

  Future<String> logout() async {
    rq.session.remove('user');
    return rq.redirect('/login');
  }

  Future<String> dashboard() async {
    var email = rq.getSession('user', def: '');
    if (email.isEmpty) return rq.redirect('/login');

    rq.addParam('email', email);
    return rq.renderView(path: 'pages/dashboard');
  }
}

Encrypted Cookies

Set safe: true on addCookie / getCookie to encrypt the value. The encryption key is FinchConfigs.cookiePassword.

// Writing
rq.addCookie('prefs', '{"theme":"dark"}', safe: true);

// Reading back
var prefs = rq.getCookie('prefs', def: '{}', safe: true);

Security: Set a strong, unique cookiePassword in your .env file. Never commit the password to version control.

Reading Cookies in Templates

{{ $e.getCookie('theme', 'light') }}