package auth import ( "net/http" "net/url" ) // Middleware resolves the current user for each request, from a session // cookie first and falling back to trusted external auth, then makes it // available via UserFromContext. type Middleware struct { Sessions *Sessions ExtAuth *ExtAuth } // Authenticate resolves the current user (session cookie, then trusted // external auth) and stores it in the request context, without requiring // one to be present. Handlers that need to know whether someone is // signed in (e.g. the login page, the home page) should use this. func (m *Middleware) Authenticate(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := m.Sessions.CurrentUser(r) if user == nil && m.ExtAuth != nil { if username := m.ExtAuth.IdentityFromRequest(r); username != "" { if u, err := m.ExtAuth.ResolveUser(username); err == nil && !u.Disabled { user = u _ = m.Sessions.Issue(w, r, u.ID) } } } if user != nil { r = r.WithContext(WithUser(r.Context(), user)) } next.ServeHTTP(w, r) }) } // RequireUser redirects unauthenticated requests to /login, preserving // the original destination. func (m *Middleware) RequireUser(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if UserFromContext(r.Context()) == nil { http.Redirect(w, r, "/login?next="+url.QueryEscape(r.URL.RequestURI()), http.StatusFound) return } next.ServeHTTP(w, r) }) } // RequireAdmin is like RequireUser but also rejects non-admin users with // 403. Authorization is enforced here, on the server, not merely by // hiding admin links in the UI. func (m *Middleware) RequireAdmin(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := UserFromContext(r.Context()) if user == nil { http.Redirect(w, r, "/login?next="+url.QueryEscape(r.URL.RequestURI()), http.StatusFound) return } if !user.IsAdmin { http.Error(w, "403 Forbidden: administrator access required", http.StatusForbidden) return } next.ServeHTTP(w, r) }) }