Files
vibesonic/internal/auth/middleware.go
T

67 lines
2.1 KiB
Go

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)
})
}