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
67 lines
1.7 KiB
Go
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
|
|
}
|