@stacksjs/browser 0.70.182 → 0.70.183

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.
@@ -0,0 +1,29 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Read the CSRF token the server planted, or null when there is none.
4
+ *
5
+ * Returns null rather than throwing outside a browser so server-side rendering
6
+ * and tests can call this freely.
7
+ */
8
+ export declare function readCsrfToken(cookieSource?: string): string | null;
9
+ /**
10
+ * Add the CSRF header to a set of request headers.
11
+ *
12
+ * A bearer token already exempts the request server-side, so the header is
13
+ * only added when there is a cookie to echo. Existing values win: an explicit
14
+ * caller-supplied token is never overwritten.
15
+ */
16
+ export declare function withCsrfHeader(headers?: Record<string, string>, cookieSource?: string): Record<string, string>;
17
+ /**
18
+ * CSRF token plumbing for browser requests.
19
+ *
20
+ * The framework's CSRF middleware is default-on for unsafe methods and uses a
21
+ * stateless double-submit cookie: the server sets a non-HttpOnly
22
+ * `X-CSRF-Token` cookie on safe responses, and the client is expected to echo
23
+ * that value back in the `X-CSRF-Token` header. Nothing in the browser package
24
+ * did the echoing, so every cookie-authenticated POST the framework itself
25
+ * issues — login, register, logout — came back 403 "CSRF token mismatch" on a
26
+ * stock install.
27
+ */
28
+ /** Cookie (and header) name the CSRF middleware issues and checks. */
29
+ export declare const CSRF_COOKIE_NAME: 'X-CSRF-Token';
@@ -0,0 +1,24 @@
1
+ export const CSRF_COOKIE_NAME = "X-CSRF-Token";
2
+ export function readCsrfToken(cookieSource) {
3
+ const raw = cookieSource ?? (typeof document < "u" ? document.cookie : "");
4
+ if (!raw)
5
+ return null;
6
+ for (const part of raw.split(";")) {
7
+ const separator = part.indexOf("=");
8
+ if (separator === -1)
9
+ continue;
10
+ if (part.slice(0, separator).trim() !== CSRF_COOKIE_NAME)
11
+ continue;
12
+ const value = part.slice(separator + 1).trim();
13
+ return value ? decodeURIComponent(value) : null;
14
+ }
15
+ return null;
16
+ }
17
+ export function withCsrfHeader(headers = {}, cookieSource) {
18
+ if (headers[CSRF_COOKIE_NAME])
19
+ return headers;
20
+ const token = readCsrfToken(cookieSource);
21
+ if (!token)
22
+ return headers;
23
+ return { ...headers, [CSRF_COOKIE_NAME]: token };
24
+ }
@@ -1,3 +1,4 @@
1
+ export * from './csrf';
1
2
  export * from './useAuth';
2
3
  export * from './useApi';
3
4
  // Re-export browser-relevant composables from @stacksjs/composables
@@ -1,3 +1,4 @@
1
+ export * from "./csrf";
1
2
  export * from "./useAuth";
2
3
  export * from "./useApi";
3
4
  export {
@@ -1,5 +1,6 @@
1
1
  import { useStorage } from "@stacksjs/composables";
2
2
  import { ref } from "@stacksjs/stx";
3
+ import { withCsrfHeader } from "./csrf";
3
4
  const token = useStorage("token", ""), stacksConfig = globalThis.__STACKS_CONFIG__ || {}, baseUrl = stacksConfig.API_URL || (typeof window < "u" ? window.location.origin : `http://localhost:${process.env.PORT_API || "3008"}`), user = ref(null), isAuthenticated = ref(!1);
4
5
  export function useAuth() {
5
6
  async function fetchAuthUser() {
@@ -44,9 +45,10 @@ export function useAuth() {
44
45
  async function register(user) {
45
46
  const url = `${baseUrl}/register`, data = await (await fetch(url, {
46
47
  method: "POST",
47
- headers: {
48
+ credentials: "same-origin",
49
+ headers: withCsrfHeader({
48
50
  "Content-Type": "application/json"
49
- },
51
+ }),
50
52
  body: JSON.stringify(user)
51
53
  })).json();
52
54
  if (isRegisterError(data))
@@ -67,9 +69,10 @@ export function useAuth() {
67
69
  try {
68
70
  const url = `${baseUrl}/login`, response = await fetch(url, {
69
71
  method: "POST",
70
- headers: {
72
+ credentials: "same-origin",
73
+ headers: withCsrfHeader({
71
74
  "Content-Type": "application/json"
72
- },
75
+ }),
73
76
  body: JSON.stringify(user)
74
77
  });
75
78
  if (!response.ok) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/browser",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.182",
5
+ "version": "0.70.183",
6
6
  "description": "Stacks core frontend/browser functionalities.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -52,7 +52,7 @@
52
52
  "prepublishOnly": "bun run build"
53
53
  },
54
54
  "dependencies": {
55
- "@stacksjs/composables": "0.70.182",
55
+ "@stacksjs/composables": "0.70.183",
56
56
  "@stacksjs/stx": "^0.2.100",
57
57
  "bun-query-builder": "^0.1.59",
58
58
  "magic-regexp": "^0.11.0"
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "better-dx": "^0.2.17",
70
- "@stacksjs/utils": "0.70.182",
70
+ "@stacksjs/utils": "0.70.183",
71
71
  "@stripe/stripe-js": "^9.10.0"
72
72
  }
73
73
  }