Skip to content

Sessions, flash, and CSRF

UI-mode projects in both project types ship encrypted cookie sessions (gin-contrib/sessions), one-shot flash messages, and CSRF protection wired into the application. SESSION_SECRET (32+ bytes) drives both the signing and encryption keys; in development an ephemeral secret is generated with a warning, outside development it is required.

// Session values
_ = session.Set(c, "theme", "dark")
theme := session.Get(c, "theme")
// Flash messages survive exactly one redirect
_ = session.PutFlash(c, "success", "Task created.")
// in the next handler:
flashes := session.Flashes(c) // returns and clears

Every state-changing request (POST/PUT/PATCH/DELETE) outside /api/ must carry the per-session CSRF token — in the _csrf form field or the X-CSRF-Token header — or it is rejected with 403 csrf_token_mismatch (constant-time comparison). Safe methods mint the token lazily.

In HTML forms, render the hidden field:

<form method="post" action="/tasks">
{{.csrf_field}}
...
</form>
c.HTML(http.StatusOK, "tasks.html", gin.H{
"csrf_field": session.TemplateField(c),
"flashes": session.Flashes(c),
})

JSON APIs under /api/ are exempt by default (they authenticate with Bearer tokens, which browsers cannot send cross-site automatically); customize with CSRFOptions.Skipper.

Projects generated with --auth --oauth also use the encrypted session for social sign-in in API mode. Once a browser is signed in, state-changing /api/ requests require the X-CSRF-Token returned by GET /api/v1/auth/csrf. Bearer-token requests remain exempt. See OAuth social sign-in.