package api import ( "bytes" "crypto/sha256" "encoding/hex" "encoding/json" "io" "net/http" "strings" "browser.local/platform/domain" "browser.local/platform/service" ) const ( runEndpointHeader = "X-Run-Endpoint" runTimestampHeader = "X-Run-Timestamp" runNonceHeader = "X-Run-Nonce" runSignatureHeader = "X-Run-Signature" ) type runRequestEnvelope struct { RunEndpointID string `json:"runEndpointId"` SessionToken string `json:"sessionToken"` } func (h *coreHandlers) requireRunSignature(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost || r.Body == nil { next(w, r) return } body, err := io.ReadAll(io.LimitReader(r.Body, 16<<20)) if err != nil { writeDecodeError(w, err) return } r.Body = io.NopCloser(bytes.NewReader(body)) var envelope runRequestEnvelope if err := json.Unmarshal(body, &envelope); err != nil { next(w, r) return } headerEndpoint := strings.TrimSpace(r.Header.Get(runEndpointHeader)) if headerEndpoint != "" && headerEndpoint != envelope.RunEndpointID { writeServiceError(w, service.ErrUnauthorized) return } bodySum := sha256.Sum256(body) err = h.core.AuthorizeRunRequestSignature(domain.RunRequestSignature{ RunEndpointID: envelope.RunEndpointID, SessionToken: envelope.SessionToken, Method: r.Method, Path: r.URL.Path, Timestamp: strings.TrimSpace(r.Header.Get(runTimestampHeader)), Nonce: strings.TrimSpace(r.Header.Get(runNonceHeader)), BodyHash: hex.EncodeToString(bodySum[:]), Signature: strings.TrimSpace(r.Header.Get(runSignatureHeader)), }) if err != nil { writeServiceError(w, err) return } next(w, r) } }