@shop-on/customer-runtime 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handoff-state.d.ts +25 -0
- package/dist/handoff-state.js +32 -0
- package/dist/session-cookie.d.ts +3 -1
- package/dist/session-cookie.js +13 -2
- package/package.json +2 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/handoff-state.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared rules for the states that bind a handoff to the browser that started
|
|
4
|
+
* it, used by their minters, the account `return_to` sanitizer that passes
|
|
5
|
+
* them through, and their checkers. The formats are a shape check only: each
|
|
6
|
+
* consumer verifies the value against its own cookie.
|
|
7
|
+
*
|
|
8
|
+
* - `checkout_state`: `ts "." base64url(HMAC-SHA256)`, minted by checkout's
|
|
9
|
+
* `/begin` and `/sign-in`.
|
|
10
|
+
* - `state` (theme): 32 random bytes base64url, minted by theme-server's
|
|
11
|
+
* `account_state` cookie.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CHECKOUT_STATE_FORMAT: RegExp;
|
|
14
|
+
export declare const ACCOUNT_STATE_FORMAT: RegExp;
|
|
15
|
+
/** Loopback: the one place a state may travel over http (local theme dev).
|
|
16
|
+
* Takes `URL#hostname`, which keeps the IPv6 brackets (`[::1]`). */
|
|
17
|
+
export declare function isLoopback(hostname: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* For every response that redirects to or from a URL holding a state: a
|
|
20
|
+
* cached copy would hand one browser's state to the next, and a Referer would
|
|
21
|
+
* carry it to whatever the next page loads. Server-only; the server module is
|
|
22
|
+
* imported here so a client-reachable importer of the formats stays clean.
|
|
23
|
+
*/
|
|
24
|
+
export declare function markResponsePrivate(): Promise<void>;
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/handoff-state.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared rules for the states that bind a handoff to the browser that started
|
|
4
|
+
* it, used by their minters, the account `return_to` sanitizer that passes
|
|
5
|
+
* them through, and their checkers. The formats are a shape check only: each
|
|
6
|
+
* consumer verifies the value against its own cookie.
|
|
7
|
+
*
|
|
8
|
+
* - `checkout_state`: `ts "." base64url(HMAC-SHA256)`, minted by checkout's
|
|
9
|
+
* `/begin` and `/sign-in`.
|
|
10
|
+
* - `state` (theme): 32 random bytes base64url, minted by theme-server's
|
|
11
|
+
* `account_state` cookie.
|
|
12
|
+
*/
|
|
13
|
+
const CHECKOUT_STATE_FORMAT = /^\d{10}\.[A-Za-z0-9_-]{43}$/;
|
|
14
|
+
const ACCOUNT_STATE_FORMAT = /^[A-Za-z0-9_-]{43}$/;
|
|
15
|
+
/** Loopback: the one place a state may travel over http (local theme dev).
|
|
16
|
+
* Takes `URL#hostname`, which keeps the IPv6 brackets (`[::1]`). */
|
|
17
|
+
function isLoopback(hostname) {
|
|
18
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* For every response that redirects to or from a URL holding a state: a
|
|
22
|
+
* cached copy would hand one browser's state to the next, and a Referer would
|
|
23
|
+
* carry it to whatever the next page loads. Server-only; the server module is
|
|
24
|
+
* imported here so a client-reachable importer of the formats stays clean.
|
|
25
|
+
*/
|
|
26
|
+
async function markResponsePrivate() {
|
|
27
|
+
const { setResponseHeader } = await import("@tanstack/react-start/server");
|
|
28
|
+
setResponseHeader("Referrer-Policy", "no-referrer");
|
|
29
|
+
setResponseHeader("Cache-Control", "no-store");
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ACCOUNT_STATE_FORMAT, CHECKOUT_STATE_FORMAT, isLoopback, markResponsePrivate };
|
package/dist/session-cookie.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* The returned `ShopBoundCookieConfig` is shape-compatible with
|
|
12
12
|
* `SealedSessionConfig` — pass it straight to `openSession`
|
|
13
|
-
* (`./sealed-session`).
|
|
13
|
+
* (`./sealed-session`). theme-server's cart and customer cookies use the
|
|
14
|
+
* same config; a theme isolate serves one shop, so they skip the payload
|
|
15
|
+
* binding. This module stays free of TanStack imports.
|
|
14
16
|
*/
|
|
15
17
|
export interface ShopBoundSession {
|
|
16
18
|
shopHandle: string;
|
package/dist/session-cookie.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
//#region src/session-cookie.ts
|
|
2
|
+
/**
|
|
3
|
+
* Storefronts on `<handle>.adorly.mn` run merchant script, and `adorly.mn` is
|
|
4
|
+
* not a public suffix, so any storefront can set a same-named cookie for the
|
|
5
|
+
* parent domain and fix a buyer into the attacker's session. Only the host
|
|
6
|
+
* itself can set a `__Host-` cookie. The prefix requires `Secure`, so
|
|
7
|
+
* plain-http dev keeps the bare name.
|
|
8
|
+
*/
|
|
9
|
+
function hostOnlyCookieName(base, secure) {
|
|
10
|
+
return secure ? `__Host-${base}` : base;
|
|
11
|
+
}
|
|
2
12
|
function createShopBoundCookieConfig(opts) {
|
|
13
|
+
const secure = opts.secure ?? process.env.NODE_ENV === "production";
|
|
3
14
|
return {
|
|
4
|
-
name: opts.name,
|
|
15
|
+
name: hostOnlyCookieName(opts.name, secure),
|
|
5
16
|
password: opts.password,
|
|
6
17
|
...opts.maxAge !== void 0 ? { maxAge: opts.maxAge } : {},
|
|
7
18
|
cookie: {
|
|
8
|
-
secure
|
|
19
|
+
secure,
|
|
9
20
|
httpOnly: true,
|
|
10
21
|
sameSite: "lax",
|
|
11
22
|
path: "/"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shop-on/customer-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "SEE LICENSE IN dist/LICENSE.md",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"./customer-session-handoff": "./dist/customer-session-handoff.js",
|
|
11
11
|
"./graphql": "./dist/graphql.js",
|
|
12
12
|
"./handoff": "./dist/handoff.js",
|
|
13
|
+
"./handoff-state": "./dist/handoff-state.js",
|
|
13
14
|
"./lease": "./dist/lease.js",
|
|
14
15
|
"./oauth": "./dist/oauth.js",
|
|
15
16
|
"./relay": "./dist/relay.js",
|