Overview
I recently built a custom order tracking page for breadtoasting.com's merchandise. We use Gooten for print-on-demand services, but their API requires an RecipeID in the URL, which poses a security risk if exposed to frontend JavaScript. To mitigate this, I added a new endpoint to our existing amp-proxy container that handles these requests securely.
Background
Gooten's API uses an RecipeID for authentication, making it easy for anyone with access to the key to place orders and have them billed to my account. This is clearly not ideal, so we needed to find a way to keep this sensitive information away from the frontend while still allowing us to track order statuses.
How It Works
I added a new endpoint to our amp-proxy container using FastAPI in Python. The container already proxies requests for Zammad (our ticketing system) and VCloud Director (for capacity stats). Here's how it works:
- Validation: The endpoint validates the
idparameter against a regular expression (^[A-Za-z0-9-]{4,64}$) to ensure only valid order IDs are processed. - Rate Limiting: A rate limit of 12 lookups per IP per hour is implemented using a sliding window mechanism to prevent enumeration attacks.
- API Call: The endpoint makes an API call to Gooten with the validated
RecipeIDandid. - Response Filtering: The raw response from Gooten includes sensitive customer information such as shipping address, email, phone number, and billing info. We filter this down to only product name, quantity, status, tracking number, tracking URL, and carrier.
- Caching: Results are cached for 30 seconds per order ID to reduce the load on Gooten's API.
Results
The frontend uses a static HTML page (order-status.html) that matches our site design. Users can enter an order ID or share a URL with ?id=12345. The page displays a progress bar for each item, showing "Received," "In Production," and "Shipped" statuses along with tracking links.
Testing Gotchas
- Non-existent Orders: Gooten returns HTTP 200 with an empty
{}body when an order does not exist. You must check if theIdfield is missing in the response, rather than relying on a 404 status. - Invalid RecipeID: Gooten returns a 403 error (
{"Message":"Invalid recipeId."}) for invalidRecipeIDs, which helps distinguish between unauthorized access and non-existent orders. - Order Listing: There is no endpoint to list all orders. GET
/orders/without an ID returns "method not supported," so you can only look up IDs that are already known, making end-to-end testing a bit awkward until real orders exist.
Lessons Learned
- Security First: Keeping sensitive information out of the frontend is crucial for security.
- Rate Limiting: Implementing rate limiting helps prevent enumeration attacks and ensures fair usage.
- API Response Handling: Carefully handling API responses to extract only necessary data can significantly reduce the risk of exposing sensitive customer information.
Deployment
To deploy this, I rebuilt the amp-proxy and homepage containers, recompiled Tailwind CSS (using a standalone CLI with no Node.js dependencies). Testing confirmed that the page returns 200 OK, /track redirects work correctly, and fake IDs return "not found" responses. The rate limit also kicked in as expected.
Conclusion
By adding this custom endpoint to our homelab setup, we've created a secure way to track order statuses without exposing sensitive customer information. This solution ensures that only authorized users can access the data they need, maintaining both security and functionality.