Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

endPoint authentication confirm token header (env.SWWF_RESKEY) #1930

Open
Tracked by #1922
humansinstitute opened this issue Nov 11, 2024 · 3 comments
Open
Tracked by #1922
Assignees

Comments

@humansinstitute
Copy link
Contributor

humansinstitute commented Nov 11, 2024

For context see: #1922

Blockers:

Update to check Token in Header of response from Stakwork includes SWWF_RESKEY

Logic

Every time a request comes to /workflow/response, a ValidateStakworkToken function runs first
It checks three things:

  1. Is there a token in the header?
  2. Is our expected token set in the environment?
  3. Do the tokens match?

If anything is wrong, it returns an error and stops

Missing token: 401 error
Server not configured: 500 error
Wrong token: 401 error

If the token is correct, it lets the request continue to the actual handler

Example test:

# Wrong - No token
curl -X POST http://your-server/workflow/response

# Wrong - Invalid token
curl -X POST \
  -H "X-Stakwork-Token: wrong-token" \
  http://your-server/workflow/response

# Correct
curl -X POST \
  -H "X-Stakwork-Token: your-actual-token" \
  http://your-server/workflow/response

Example Token Handler:

// Simple Version
func ValidateStakworkToken(c *fiber.Ctx) error {
    // Get token from header
    token := c.Get("X-Stakwork-Token")
    
    // Get environment variable
    expectedToken := os.Getenv("SWWF_RESKEY")
    
    // Compare tokens
    if token != expectedToken {
        return c.Status(401).JSON(fiber.Map{
            "error": "invalid token",
        })
    }
    
    // Continue to next handler if token is valid
    return c.Next()
}

// More Complete Version
func ValidateStakworkToken(logger *zap.Logger) fiber.Handler {
    return func(c *fiber.Ctx) error {
        // Get token from header
        token := c.Get("X-Stakwork-Token")
        
        // Check if token is present
        if token == "" {
            logger.Warn("missing authentication token")
            return c.Status(401).JSON(fiber.Map{
                "error": "missing authentication token",
                "code":  "MISSING_TOKEN",
            })
        }
        
        // Get expected token from environment
        expectedToken := os.Getenv("SWWF_RESKEY")
        if expectedToken == "" {
            logger.Error("SWWF_RESKEY not set in environment")
            return c.Status(500).JSON(fiber.Map{
                "error": "server configuration error",
                "code":  "SERVER_ERROR",
            })
        }
        
        // Compare tokens using secure comparison
        if !hmac.Equal([]byte(token), []byte(expectedToken)) {
            logger.Warn("invalid token provided",
                zap.String("ip", c.IP()),
                zap.String("path", c.Path()))
            return c.Status(401).JSON(fiber.Map{
                "error": "invalid authentication token",
                "code":  "INVALID_TOKEN",
            })
        }
        
        // Token is valid, continue
        return c.Next()
    }
}

// Usage in your main.go or routes setup
func SetupRoutes(app *fiber.App, logger *zap.Logger) {
    // Create a group for workflow endpoints
    workflow := app.Group("/workflow")
    
    // Add the token validation middleware to the response endpoint
    workflow.Post("/response",
        ValidateStakworkToken(logger),  // Validate token first
        HandleWorkflowResponse,         // Then handle the response
    )
}

// Example handler for workflow response
func HandleWorkflowResponse(c *fiber.Ctx) error {
    // Parse the response body
    var response struct {
        RequestUUID string          `json:"request_uuid"`
        Response    string          `json:"response"`
        Data       json.RawMessage `json:"data"`
    }
    
    if err := c.BodyParser(&response); err != nil {
        return c.Status(400).JSON(fiber.Map{
            "error": "invalid request body",
            "code":  "INVALID_BODY",
        })
    }
    
    // Process the workflow response...
    
    return c.Status(200).JSON(fiber.Map{
        "status": "success",
    })
}
@MirzaHanan
Copy link
Contributor

@humansinstitute assign me?

@aliraza556
Copy link
Contributor

@humansinstitute, please assign me?

@humansinstitute
Copy link
Contributor Author

@tobi-bams will discuss later. Just want to walk through this design

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants