Firefox扩展PUT请求携带凭证问题:模拟沃尔玛加购请求
Hey there, let's figure out why your PUT request to Walmart's cart API isn't working from your Firefox extension's content script. The issue is almost certainly missing critical request headers that the site requires to authenticate the request—just setting withCredentials=true isn't enough for most modern e-commerce APIs. Here's how to fix it:
1. Capture all required request headers from a successful manual request
When you click the "Add to Cart" button manually, your browser sends a bunch of headers that Walmart uses to verify the request is legitimate. You need to replicate all of these in your XHR call.
Open Firefox DevTools (F12), go to the Network tab, filter for the PUT request to /api/v3/cart/:GCRT/items, then check the "Headers" tab. Look for headers like:
Authorization(usually a Bearer token)X-Csrf-Tokenor similar anti-forgery tokenUser-Agent(must match your browser's exact user agent string)Accept(matches the content types the API expects)- Any custom Walmart-specific headers (like
X-Walmart-*or similar)
Your current code only sets Content-Type—you need to add all these missing headers with their exact values from the successful request.
2. Dynamically fetch dynamic tokens (don't hardcode!)
Tokens like the CSRF token or cart ID (:GCRT in the URL) are dynamic and change per session/user. You can't hardcode them—you need to extract them from the page:
- Cart ID: Look for it in the page's URL, DOM elements (like a data attribute), or in a previous API response stored in
localStorage/sessionStorage. - CSRF Token: Check the page's
<meta>tags (common for frameworks like React/Angular) or extract it from a cookie. For example:// Fetch CSRF token from meta tag const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content; // Or from a cookie (use a helper function to parse cookies) function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } const csrfToken = getCookie('csrfToken');
3. Fix your extension's permissions
Make sure your manifest.json includes the necessary host permissions to send requests to Walmart's API. Add this line:
"permissions": [ "https://grocery.walmart.com/*", "cookies" // If you need to access cookies directly ]
Without these permissions, Firefox will block your request even if the headers are correct.
4. Consider using a background script instead of content script
Content scripts run in the page's context and have some limitations around cross-origin requests (even same-origin can have restrictions for certain headers). A background script has more permissions and can handle requests more reliably:
- Send a message from your content script to the background script with the cart item details.
- The background script sends the PUT request with all necessary headers and credentials.
Example background script code:
browser.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.action === 'addToCart') { fetch(`https://grocery.walmart.com/api/v3/cart/${message.cartId}/items`, { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json; charset=utf-8', 'Authorization': message.authToken, 'X-Csrf-Token': message.csrfToken, 'User-Agent': navigator.userAgent // Add all other required headers here }, body: JSON.stringify(message.cartItems) }) .then(res => res.json()) .then(data => sendResponse(data)) .catch(err => sendResponse({ error: err.message })); return true; // Keep the message channel open for async response } });
5. Verify your request URL
The :GCRT in your URL is a placeholder—you need to replace it with the actual cart ID for the logged-in user. You can find this ID in the manual request's URL (from DevTools) or extract it from the page's state.
Modified Content Script Example
Here's how your content script might look after adding all these fixes:
(function() { // 1. Extract dynamic values from the page const cartId = document.querySelector('[data-cart-id]')?.dataset.cartId; // Example DOM extraction const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content; const authToken = localStorage.getItem('walmart_auth_token'); // Example storage extraction if (!cartId || !csrfToken || !authToken) { console.error('Missing required tokens/cart ID'); return; } const params = {"cartItems":[{"offerId":"8E5AE39B44CD467DAC4AAA3A0042110F","quantity":3}]}; const xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { const res = JSON.parse(xhttp.responseText); console.log('Success:', res); } else { console.error('Request failed:', this.status, this.responseText); } } }; xhttp.open("PUT", `https://grocery.walmart.com/api/v3/cart/${cartId}/items`, true); xhttp.withCredentials = true; // Add all required headers xhttp.setRequestHeader("Content-Type", "application/json; charset=utf-8"); xhttp.setRequestHeader("Authorization", `Bearer ${authToken}`); xhttp.setRequestHeader("X-Csrf-Token", csrfToken); xhttp.setRequestHeader("User-Agent", navigator.userAgent); // Add other headers from your manual request here xhttp.send(JSON.stringify(params)); })();
The key takeaway is that modern APIs rely on multiple layers of authentication (cookies + tokens + custom headers) to prevent abuse. You need to replicate the exact request the browser sends when you click the button—every header, every dynamic value.
内容的提问来源于stack exchange,提问作者Vlada

