Go Quickstart
Wire LumoAuth into a Go web app using the official
lumo-auth-go SDK. The WebAuth.Router()
mounts /login, /callback, /logout; WebAuth.Middleware populates the
request context with the signed-in user; lumoauth.UserFromContext(ctx)
reads it back out.
Prerequisites
- Go 1.22+
- An OAuth client registered with redirect URI
http://localhost:3000/auth/callback.
Register the client in the portal under Applications → New application: enter an Application name, choose Web application, enter the Redirect URI, and click Create application. The client secret is shown once, on the page you land on.

The Quickstarts page (under Applications & developer tools) has a Go tab that shows the .env values for your organization and client pre-filled, ready to copy.

The tab's first step lists only OAuth clients of the kind these snippets need; if none exists, Create one for this framework opens the create form with the right type preselected and brings you back. Its last step, Check it worked, shows the first sign-in that completes through the selected client.
Or scaffold with the CLI:
lumo login --org your-org-slug
lumo init --framework go --dir ./my-app
cd ./my-app && go mod tidy && go run .
Manual install
go get github.com/lumoauth/lumo-auth-go \
github.com/go-chi/chi/v5 \
github.com/joho/godotenv
.env:
LUMO_BASE_URL=https://app.lumoauth.dev
LUMO_ORG_ID=your-org-slug
LUMO_CLIENT_ID=your-oauth-client-id
LUMO_CLIENT_SECRET=your-oauth-client-secret
SESSION_SECRET=change-me-to-a-random-32-byte-string
PORT=3000
main.go:
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
"github.com/joho/godotenv"
lumoauth "github.com/lumoauth/lumo-auth-go"
)
func main() {
_ = godotenv.Load()
auth, err := lumoauth.NewWebAuth(lumoauth.WebConfig{
BaseURL: os.Getenv("LUMO_BASE_URL"),
Organization: os.Getenv("LUMO_ORG_ID"),
ClientID: os.Getenv("LUMO_CLIENT_ID"),
ClientSecret: os.Getenv("LUMO_CLIENT_SECRET"),
SessionSecret: os.Getenv("SESSION_SECRET"),
CallbackPath: "/auth/callback",
PostLoginRedirect: "/",
// Session cookie defaults to Secure: true, so browsers won't send
// it over plain HTTP. Opt out for localhost dev; remove this line
// (or use lumoauth.Bool(true)) in production over HTTPS.
CookieSecure: lumoauth.Bool(false),
})
if err != nil {
log.Fatal(err)
}
r := chi.NewRouter()
r.Mount("/auth", auth.Router())
// Middleware reads the signed-in user from the session into the
// request context. UserFromContext returns nil for anonymous requests.
r.Group(func(r chi.Router) {
r.Use(auth.Middleware)
r.Get("/", func(w http.ResponseWriter, req *http.Request) {
user := lumoauth.UserFromContext(req.Context())
if user == nil {
http.Redirect(w, req, "/auth/login", http.StatusFound)
return
}
fmt.Fprintf(w, "<h1>Hi %s</h1><a href=\"/auth/logout\">Sign out</a>", user.Email)
})
// Gate with RequireAuth — 401 if no user.
r.With(auth.RequireAuth).Get("/api/me", func(w http.ResponseWriter, req *http.Request) {
user := lumoauth.UserFromContext(req.Context())
fmt.Fprintf(w, "%s\n", user.Email)
})
})
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
log.Printf("listening on http://localhost:%s", port)
log.Fatal(http.ListenAndServe(":"+port, r))
}
Run with go run ., visit http://localhost:3000 — you'll be
redirected to LumoAuth, sign in, and bounce back with the user in
context.
net/http (no chi)
The router and middleware are standard http.Handler types — the chi
example above is for ergonomic grouping, but plain net/http works just
as well:
mux := http.NewServeMux()
// Strip /auth prefix and delegate to auth.Router() on each subpath.
mux.Handle("/auth/", http.StripPrefix("/auth", auth.Router()))
mux.Handle("/", auth.Middleware(http.HandlerFunc(homeHandler)))
mux.Handle("/api/me", auth.Middleware(auth.RequireAuth(http.HandlerFunc(meHandler))))
http.ListenAndServe(":3000", mux)
Behind a reverse proxy
The middleware honours X-Forwarded-Proto and X-Forwarded-Host when
constructing the absolute callback URL, so you can run the Go process
on plain HTTP behind a TLS-terminating proxy and the redirect URI sent
to LumoAuth still resolves to your public https://... URL.
Production checklist
-
SessionSecretis ≥32 bytes of cryptographic randomness. - The session cookie's
Secureflag is on. The SDK defaultsCookieSecuretotrue; setCookieSecure: lumoauth.Bool(false)only for plain-http local dev, and leave it unset (ortrue) in production. - Rotate
SessionSecretperiodically — gorilla/sessions accepts multiple keys for graceful rotation. - OAuth client's redirect URI exactly matches the production URL.
- If running behind a proxy, configure it to set
X-Forwarded-Proto: https.
Reference
- Go SDK API reference
- chi documentation
lumo init --framework go— scaffold a complete starter project.