Go bananas auth
Use when adding authentication to a go-bananas (github.com/mikehelmick/go-bananas) app — implementing the Authenticator interface, gating routes with RequireAuthenticated, or wiring an OIDC authorization-code flow. Triggers on "go-bananas auth", "protect a route", "implement Authenticator", "add OIDC/login to go-bananas".From its SKILL.md
npx -y skills add mikehelmick/go-bananas --skill go-bananas-authAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.1 KB, as published. Nobody here has run it
Authentication in go-bananas
go-bananas has exactly one auth seam: the middleware.Authenticator interface.
The framework depends on no identity provider — you implement the seam and gate
routes with it.
type Authenticator interface {
// (nil, nil) = anonymous → 401; non-nil error → 500; non-nil principal → allowed.
Authenticate(r *http.Request) (principal any, err error)
}
Gate routes
me := r.NewRoute().Subrouter()
me.Use(middleware.RequireAuthenticated(myAuth, h))
me.HandleFunc("/me", profile)
On success the principal is stored on the context; read it anywhere with
webctx.PrincipalFromContext(ctx) (type-assert to your own user type).
Authorization (403 vs 401)
RequireAuthenticated only answers "who is this?" — permission checks belong in
your handlers or your own middleware. Once a principal exists, deny with 403,
not 401:
if !user.CanAdminister(course) {
response.Forbidden(w, r, h) // renders the "403" template / {"error":"forbidden"}
return
}
Use response.Unauthorized only for missing or invalid credentials. Ship a
403.html template alongside 401.html, or use response.NotFound when the
existence of the resource is itself privileged (cross-tenant lookups).
Session-backed authenticator
The simplest implementation reads a user that a login flow stored on the session:
type SessionAuthenticator struct{}
func (SessionAuthenticator) Authenticate(r *http.Request) (any, error) {
s := webctx.SessionFromContext(r.Context())
if s == nil {
return nil, nil
}
u, _ := s.Values["user"].(*User) // gob.Register(&User{}) once, in init()
return u, nil // nil ⇒ anonymous
}
Register your principal type with gob.Register(&User{}) so it survives the
secure-cookie session.
OIDC (authorization-code flow)
Keep the OIDC client (github.com/coreos/go-oidc/v3 + golang.org/x/oauth2) in
a separate module so it never enters the core module graph. The framework
already provides the session, CSRF, secure-header, and idle-timeout plumbing.
GET /login— generatestate+nonce, store on the session, redirect tooauth2Config.AuthCodeURL(state, oidc.Nonce(nonce)).GET /auth/callback— verifystate, exchange the code, verify the ID token viaprovider.Verifier(...).Verify, checknonce, extract claims, store your*Useron the session, redirect.Authenticatereads that user back (as above).
The full, working implementation is in
github.com/mikehelmick/go-bananas/examples/ssr-oidc/auth.go. Mirror its
structure. Note that mutating endpoints (including any dev-login POST) are
CSRF-protected, so they need {{ .csrfField }}.
Full API: https://pkg.go.dev/github.com/mikehelmick/go-bananas/middleware#Authenticator