package server import ( "net/http" "strings" ) // authenticateAgent resolves the agent identified by the Bearer token on the // request, verifying the token against the stored hash in constant time. // Returns nil if authentication fails. func (s *Server) authenticateAgent(r *http.Request) *Agent { id := r.Header.Get("X-Agent-Id") token := bearerToken(r) if id == "" || token == "" { return nil } a, err := s.db.GetAgent(id) if err != nil || a == nil { return nil } if !constantTimeEqual(a.TokenHash, hashToken(token)) { return nil } return a } func bearerToken(r *http.Request) string { h := r.Header.Get("Authorization") if strings.HasPrefix(h, "Bearer ") { return strings.TrimPrefix(h, "Bearer ") } return "" } // enroll creates a new agent record for a valid enroll secret and returns the // generated id plus a one-time plaintext token (only the hash is stored). func (s *Server) enroll(name, hostname, os, arch, version string) (id, token string, err error) { id = randToken()[:16] token = randToken() a := Agent{ ID: id, Name: firstNonEmpty(name, hostname, id), Hostname: hostname, OS: os, Arch: arch, Version: version, TokenHash: hashToken(token), } if err = s.db.CreateAgent(a); err != nil { return "", "", err } return id, token, nil } func firstNonEmpty(vals ...string) string { for _, v := range vals { if strings.TrimSpace(v) != "" { return v } } return "" }