Files
NexaVPN/backend/internal/httpserver/middleware.go
nessi 830491cb0d chore: initial project scaffold with admin web, backend, desktop client, and deployment setup
Add monorepo structure for NexaVPN WireGuard control plane including:
- .gitignore for node_modules, build artifacts, and environment files
- README with project overview, monorepo layout, and quick start guide
- Admin web UI with React, Vite, TypeScript, and nginx reverse proxy
- API client with type definitions for users, devices, policies, gateways, and audit logs
- Admin pages for dashboard, users, devices, policies, g
2026-03-15 16:32:34 +01:00

67 lines
1.7 KiB
Go

package httpserver
import (
"context"
"net/http"
"strings"
"github.com/go-chi/chi/v5/middleware"
"github.com/google/uuid"
"github.com/nexavpn/nexavpn/backend/internal/apiutil"
"github.com/nexavpn/nexavpn/backend/internal/auth"
)
type contextKey string
const claimsContextKey contextKey = "claims"
func BaseMiddleware(next http.Handler) http.Handler {
return middleware.RealIP(middleware.RequestID(middleware.Logger(next)))
}
func AuthMiddleware(jwtSecret string) 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 ") {
apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "missing bearer token")
return
}
claims, err := auth.ParseAccessToken(jwtSecret, strings.TrimPrefix(header, "Bearer "))
if err != nil {
apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "invalid access token")
return
}
ctx := context.WithValue(r.Context(), claimsContextKey, claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func AdminOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := ClaimsFromContext(r.Context())
if !ok || claims.Role != "admin" {
apiutil.Error(w, http.StatusForbidden, "forbidden", "admin role required")
return
}
next.ServeHTTP(w, r)
})
}
func ClaimsFromContext(ctx context.Context) (auth.Claims, bool) {
claims, ok := ctx.Value(claimsContextKey).(auth.Claims)
return claims, ok
}
func MustUserID(ctx context.Context) (uuid.UUID, bool) {
claims, ok := ClaimsFromContext(ctx)
if !ok {
return uuid.Nil, false
}
return claims.UserID, true
}