Go backend with domain-based packages (volunteer, schedule, timeoff, checkin, notification), SQLite storage, JWT auth, and chi router. React TypeScript frontend with routing, auth context, and pages for all core features. Multi-stage Dockerfile and docker-compose included. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
50 lines
1.3 KiB
Go
50 lines
1.3 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"git.unsupervised.ca/walkies/internal/auth"
|
|
"git.unsupervised.ca/walkies/internal/respond"
|
|
)
|
|
|
|
type contextKey string
|
|
|
|
const claimsKey contextKey = "claims"
|
|
|
|
func Authenticate(authSvc *auth.Service) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
header := r.Header.Get("Authorization")
|
|
if !strings.HasPrefix(header, "Bearer ") {
|
|
respond.Error(w, http.StatusUnauthorized, "missing or invalid authorization header")
|
|
return
|
|
}
|
|
claims, err := authSvc.Parse(strings.TrimPrefix(header, "Bearer "))
|
|
if err != nil {
|
|
respond.Error(w, http.StatusUnauthorized, "invalid token")
|
|
return
|
|
}
|
|
ctx := context.WithValue(r.Context(), claimsKey, claims)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
}
|
|
|
|
func RequireAdmin(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
claims := ClaimsFromContext(r.Context())
|
|
if claims == nil || claims.Role != "admin" {
|
|
respond.Error(w, http.StatusForbidden, "admin access required")
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func ClaimsFromContext(ctx context.Context) *auth.Claims {
|
|
claims, _ := ctx.Value(claimsKey).(*auth.Claims)
|
|
return claims
|
|
}
|