Authentication ¶
Cicadel has one core accounts service and two explicit HTTP adapters. Browser applications use server-rendered HTML, server-side SCS sessions and CSRF checks. API clients can use the JSON bearer adapter. Both adapters consult the same PostgreSQL users, account sessions, recovery tokens, groups and permissions. Disabling an account, resetting its password or revoking its sessions affects both adapters. A browser never receives the account bearer token in HTML or JSON.
Ask your agent ¶
Read https://cicadel.fauzaanu.com/en/dev/topics/authentication/index.md.
Add browser sign-in to my application using the accounts starter or adapter.
Use PostgreSQL sessions, CSRF protection and verified recovery addresses.
Show the registration and route protection code. Test sign-in, sign-out,
and rejection of revoked sessions; keep secrets outside source code.Create the starter ¶
From the framework checkout:
go build -o bin/cicadel ./cmd/cicadel
./bin/cicadel startproject /tmp/myapp example.com/myapp
cd /tmp/myapp
go mod edit -replace=github.com/fauzaanu/cicadel=/absolute/path/to/Cicadel
go mod edit -replace=github.com/fauzaanu/cicadel/contrib/accounts=/absolute/path/to/Cicadel/contrib/accounts
go mod edit -replace=github.com/fauzaanu/cicadel/contrib/formsets=/absolute/path/to/Cicadel/contrib/formsets
go mod edit -replace=github.com/fauzaanu/cicadel/contrib/migrations=/absolute/path/to/Cicadel/contrib/migrations
go mod edit -replace=github.com/fauzaanu/cicadel/contrib/querygen=/absolute/path/to/Cicadel/contrib/querygen
go mod tidy
export DATABASE_URL='postgres://USER:PASSWORD@localhost/myapp?sslmode=disable'
export CICADEL_DEBUG=true
export CICADEL_SECURE_COOKIES=false
go run manage.go migrate
CICADEL_USER_PASSWORD='a local account password' go run manage.go createuser owner@example.com
go run manage.go runserverUse an email address whose ownership you have verified before provisioning the
account. This starter treats the stored username as that verified recovery
address. Requests cannot supply a separate delivery address. There is no public
registration endpoint or administrator panel. The service’s Create API remains
available for application-owned provisioning and invitation workflows.
The accounts module has no published release yet. The commands above use local checkout paths; commit your resolved Go dependencies and replace machine-specific paths before distributing the application. startproject always includes accounts, formsets and the standard migration workflow; runtime commands require PostgreSQL. The old --accounts spelling is a compatibility alias for the same starter.
Screens and actions ¶
The default prefix is /accounts; accounts.Pages.Prefix can change it.
| Route | GET | POST |
|---|---|---|
/accounts | Signed-in account and session actions | — |
/accounts/login | Login form | Rotate session and sign in |
/accounts/recover | Recovery form | Deliver a reset link to the verified destination |
/accounts/reset | Reset form with token | Consume the token once and replace the password |
/accounts/change-password | Current/new password form | Replace password and revoke all sessions |
/accounts/logout | — | Revoke current account session and destroy its cookie session |
/accounts/revoke | — | Revoke all account sessions and sign out |
/accounts/style.css | Embedded compiled stylesheet | — |
To customize the screens, compose your own pages with contrib/accounts/accountui.
Integrate an existing application ¶
In your project setup, create the service and browser adapter, then register the returned app:
service, err := accounts.New(accounts.Options{
DB: db,
DeliverRecovery: DeliverRecovery,
})
if err != nil {
return err
}
browser, err := service.Browser(security.NewSessions(store, true))
if err != nil {
return err
}
authApp, err := browser.HTMLApp(accounts.Pages{
Brand: "My application",
Logger: logger,
})
if err != nil {
return err
}
installed = append(installed, authApp)This setup fragment uses your PostgreSQL db, shared session store, logger, recovery callback and existing []cicadel.App. Pass installed to the project’s cicadel.New call. Register the adapter once and run migrate before serving. The generated accounts starter includes the full setup in project/application.go.
Protect another app’s handler with the browser adapter:
protected := browser.Middleware(browser.RequireLogin(http.HandlerFunc(dashboard)))
route := urls.Path("GET", "/dashboard", "dashboard:index", protected.ServeHTTP)Add route to that app’s routes. Import net/http and github.com/fauzaanu/cicadel/urls; dashboard is your handler. Check object-level permissions inside it.
HTMLApp owns LoadAndSave → CSRF → account-resolution middleware for its routes.
Do not wrap those routes with those same middleware again. For another app’s
protected routes, apply browser.Middleware then browser.RequireLogin, and
use accounts.UserFromContext inside handlers. RequireLogin checks a freshly
resolved account, rather than only a stored SCS user ID. Object permissions still
require explicit application policy and scoped database queries.
The starter uses PostgreSQL accounts_browser_sessions through SCS’s configurable
table name. Migration 002 adds the table without rewriting the existing accounts
history. A custom store may also be supplied. Sessions are HttpOnly, SameSite=Lax,
secure by default, rotated on login and cleared at logout. Account expiry and
revocation are checked on every browser request. SCS expiry can end a session
before the associated account token expires; accounts:cleanup removes expired
tokens. Browser forms are bounded to 8 KiB and all unsafe methods require CSRF.
For API-only clients use service.HTTPApp(prefix) and service.Middleware with
explicit Authorization: Bearer …. Register either adapter once under the
accounts app name. If a host needs both, combine their routes into that one app
and scope cookie middleware to the HTML routes; never apply cookie CSRF middleware
to a bearer-only API by accident.
Recovery delivery and deployment ¶
Configure these starter environment variables:
| Variable | Purpose |
|---|---|
CICADEL_PUBLIC_URL | HTTPS origin used to construct reset links |
CICADEL_MAIL_FROM | Sender mailbox |
CICADEL_SMTP_ADDRESS | SMTP host:port with STARTTLS |
CICADEL_SMTP_USERNAME, CICADEL_SMTP_PASSWORD | Optional SMTP credentials |
CICADEL_LOG_FORMAT, CICADEL_LOG_LEVEL | JSON/text format and log threshold |
project.DeliverRecovery is the application-owned callback. It sends to the
stored verified address, constructs a local reset route using the configured
origin and passes the token only to the mail transport. Known and unknown users
receive the same recovery response. Delivery errors are logged without tokens,
addresses or server response text. Missing delivery configuration is handled
without revealing whether a username exists. Passwords must contain 12–72 bytes;
applications may add their own password policy.
Use HTTPS and secure cookies, configure allowed hosts, apply gateway rate limits
to login/recovery endpoints and schedule manage.go accounts:cleanup. Never log
query strings on reset routes at the proxy. Reset pages send no-referrer,
no-store and a restrictive content security policy. See email,
logging and operations for configuration details.