package auth import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" ) type User struct { Email string `json:"email"` Roles []string `json:"roles"` } func (u *User) HasRole(role ...string) bool { for _, r := range role { for _, o := range u.Roles { if r == o { return true } } } return false } type ContextKey string const ( UserKey = ContextKey("user") ) func UserMiddleware(signingKey []byte) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := &User{} header := r.Header.Get("user") if len(header) == 0 { fmt.Println("User not found in request headers, check api-gateway") next.ServeHTTP(w, r) return } if len(signingKey) > 0 { signature := r.Header.Get("user-signature") if signature == "" { http.Error(w, "missing user-signature header", http.StatusUnauthorized) return } mac := hmac.New(sha256.New, signingKey) mac.Write([]byte(header)) expected := hex.EncodeToString(mac.Sum(nil)) if !hmac.Equal([]byte(signature), []byte(expected)) { http.Error(w, "invalid user-signature", http.StatusUnauthorized) return } } if err := json.Unmarshal([]byte(header), &user); err != nil { fmt.Printf("User in header (%s) not parseable, check api-gateway", header) next.ServeHTTP(w, r) } else { ctx := context.WithValue(r.Context(), UserKey, user) next.ServeHTTP(w, r.WithContext(ctx)) } }) } } func FromContext(ctx context.Context) *User { if user := ctx.Value(UserKey); user != nil { if u, ok := user.(*User); ok { return u } fmt.Println("User in context is not the correct type") } return nil }