Sessions
Learn how to manage user sessions and maintain state in Suave applications.
Cookie-Based Sessions
Use cookies to maintain session state:
open Suave
open Suave.Operators
open Suave.Filters
open Suave.Successful
open Suave.Writers
let app =
choose [
path "/set-session" >=>
setHeader "Set-Cookie" "session=user123; Path=/; HttpOnly" >=>
OK "Session set"
path "/get-session" >=> fun ctx ->
match ctx.request.header "Cookie" with
| Choice1Of2 cookies ->
OK (sprintf "Cookies: %s" cookies) ctx
| Choice2Of2 _ ->
OK "No session" ctx
]
Session Storage Patterns
Store session data with in-memory or database backends:
open Suave
open Suave.Operators
open Suave.Filters
open Suave.Successful
open Suave.Writers
open System.Collections.Concurrent
open Suave.RequestErrors
type SessionData = { userId : int; username : string }
// In-memory session store
let sessions = new ConcurrentDictionary<string, SessionData>()
let app =
choose [
POST >=> path "/login" >=> fun ctx ->
async {
let sessionId = System.Guid.NewGuid().ToString()
let sessionData = { userId = 1; username = "john" }
sessions.TryAdd(sessionId, sessionData) |> ignore
return!
(setHeader "Set-Cookie"
(sprintf "sessionId=%s; Path=/; HttpOnly" sessionId) >=>
OK "Session created") ctx
}
path "/profile" >=> fun ctx ->
match ctx.request.header "cookie" with
| Choice1Of2 cookie ->
let sessionId = cookie.Split('=').[1]
match sessions.TryGetValue(sessionId) with
| true, sessionData ->
OK (sprintf "Session data: %A" sessionData) ctx
| false, _ ->
RequestErrors.UNAUTHORIZED "Invalid session" ctx
| Choice2Of2 _ ->
RequestErrors.UNAUTHORIZED "No session" ctx
]
Session Timeout
Implement session expiration:
open Suave
open Suave.Operators
open System
open Suave.Filters
open Suave.Successful
open Suave.Writers
let sessionTimeout = TimeSpan.FromMinutes(30.0)
let app =
choose [
path "/login" >=>
setHeader "Set-Cookie"
(sprintf "session=xyz; Path=/; Max-Age=%d; HttpOnly"
(int sessionTimeout.TotalSeconds)) >=>
OK "Session created with 30 minute timeout"
]
Secure Session Cookies
Create secure session cookies for production:
open Suave
open Suave.Operators
open Suave.Writers
open Suave.Filters
open Suave.Successful
let createSecureSessionCookie (sessionId: string) =
sprintf "session=%s; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=1800" sessionId
let app =
choose [
path "/secure-login" >=>
setHeader "Set-Cookie" (createSecureSessionCookie "session-xyz") >=>
OK "Secure session created"
]
Session Cleanup
Clean up expired sessions:
open Suave
open System
open System.Collections.Concurrent
open Suave.Successful
let sessions = new ConcurrentDictionary<string, (obj * DateTime)>()
let cleanupExpiredSessions () =
async {
while true do
do! Async.Sleep(60000) // Every minute
let now = DateTime.UtcNow
let expired =
sessions
|> Seq.filter (fun kvp -> (snd kvp.Value - now).TotalMinutes > 30.0)
|> Seq.map (fun kvp -> kvp.Key)
|> Seq.toList
expired |> List.iter (fun key -> sessions.TryRemove(key) |> ignore)
printfn "Cleaned up %d expired sessions" expired.Length
}
[<EntryPoint>]
let main argv =
// Start cleanup task
Async.Start (cleanupExpiredSessions())
let app = OK "Sessions with cleanup"
startWebServer defaultConfig app
0
CSRF Token Sessions
Protect forms with CSRF tokens in sessions:
open Suave
open Suave.Operators
open System.Collections.Concurrent
open Suave.Filters
open Suave.Successful
open Suave.RequestErrors
let csrfTokens = new ConcurrentDictionary<string, string>()
let generateCSRFToken sessionId =
let token = System.Guid.NewGuid().ToString()
csrfTokens.AddOrUpdate(sessionId, token, fun _ _ -> token) |> ignore
token
let validateCSRFToken sessionId token =
match csrfTokens.TryGetValue(sessionId) with
| true, storedToken -> storedToken = token
| false, _ -> false
let app =
choose [
GET >=> path "/form" >=> fun ctx ->
let sessionId = "session-123"
let token = generateCSRFToken sessionId
let html = sprintf "<input type='hidden' name='csrf_token' value='%s'>" token
OK html ctx
POST >=> path "/form" >=> fun ctx ->
match ctx.request.formData "csrf_token" with
| Choice1Of2 token when validateCSRFToken "session-123" token ->
OK "Form submitted safely" ctx
| _ ->
RequestErrors.FORBIDDEN "CSRF token invalid" ctx
]