Customer access and invitations
Require a signed-in shopper or a narrow, expiring invitation before a work runs.
Choose the proof your build needs
Use auth: "customer" for signed-in storefront customers, or auth: "token" for an invitation that permits a specific action. Public works accept anonymous requests. Merchant auth is for merchants and connected agents. All customer-facing modes use automatic traffic protection; logging in does not exempt a caller from limits.
A signed-in storefront customer
Declare an HTTP trigger with auth: "customer", GET or POST, and the exact storefront origin. The shared Shopify app proxy forwards /apps/littleworks/preview/your.work or /apps/littleworks/production/your.work to Littleworks. One proxy serves every work. Shopify can customize the proxy path; verify the installed path before wiring the frontend. Littleworks requires write_app_proxy, separately from optional Shopify data permissions.
Littleworks verifies Shopify’s query signature, timestamp and store, then requires a nonempty customer ID. The work receives context.customer.id as a Shopify customer GID. The direct /f/... URL and invoke_function cannot supply customer identity. GET input excludes Shopify’s proxy parameters. POST accepts JSON.
export default async ({ context, db }) => {
return db.collection("requests").list({
where: { customerId: context.customer.id },
});
};const response = await fetch("/apps/littleworks/preview/customer.requests", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
if (response.status === 401) {
// Ask the shopper to sign in using this store's customer login flow.
}This mode supports storefront sessions where Shopify supplies logged_in_customer_id. Test the store’s actual customer-account setup; if Shopify supplies no customer ID, the request fails closed. Customer-account UI extensions and headless clients require separate identity integration and cannot use this mode to impersonate a shopper. CORS and Origin are not proof of identity.
An invitation for a guest
First deploy the receiving HTTP work with auth: "token". Then use a private callable or merchant HTTP work to issue an invitation after checking eligibility. A public, customer or token work cannot mint invitations, even when invoked through an agent. The issuer stays in the same store and environment as the target.
const invitation = await auth.issueToken({
work: "reviews.submit",
claims: { orderId: verifiedOrderId, productId: verifiedProductId },
expiresInSeconds: 86400,
singleUse: true,
});
// Deliver invitation.token only to its intended recipient through an
// authorized integration. Never log it or store it in a public record.The result contains token and expiresAt. Tokens are opaque bearer credentials: anyone possessing one can perform its permitted action. Only a token hash and encrypted claims are stored by Littleworks. Defaults are one hour and single use; expiry may be 60 seconds to seven days. Claims are limited to 4 KiB and stores may have 1,000 unexpired tokens, including consumed tokens until expiry. Keep claims minimal, using IDs rather than personal details.
export default async ({ input, context, db }) => {
const { orderId, productId } = context.action.claims;
return db.collection("reviews").create({
orderId,
productId,
rating: input.rating,
status: "pending",
});
};Send the token in Authorization: Bearer <token> to the target URL returned by deployment. It is never accepted from an input field or query parameter. For an email invitation, link to the build’s frontend with the token in the URL fragment; the frontend should read it, remove it from the address bar and send it in the Authorization header. Littleworks does not host that frontend. Fragments still need care: avoid exposing them to analytics or other scripts.
Each token binds to the exact store, environment, work, method and deployed version. Deploying new code invalidates invitations for previous versions; restoring that exact version can make its unexpired, unused invitations valid again. Pause the work to stop all new executions. Choose a reusable token only for a build that needs repeated access, with a suitably short expiry.
Input and admission failures do not consume a single-use token. Admission and consumption are atomic, so concurrent requests cannot use it twice. Once execution starts, it stays consumed even if the work fails or returns an error, because earlier effects may have completed. Inspect the run before issuing another invitation. Tokens and customer login do not make external effects transactional.
Automatic protection
Littleworks limits bursts, simultaneous runs, per-work and store execution, stored data, and external requests. See Limits and security. Rejected admissions create no run and spend no run allowance. Littleworks HTTP 429 for a time-based allowance includes Retry-After and a retry time; storage needs space freed instead. Edge flood blocks may return a non-JSON response and appear only in Cloudflare security events. Back off and never automatically replay a mutation. Shopify shows traffic notices on affected works and explanations on failed runs.