@mindfulauth/core 1.2.1 → 2.0.0-beta.3

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/README.md CHANGED
@@ -1,18 +1,6 @@
1
- # @mindful-auth/core
1
+ # @mindful-auth/core - ASTRO 6 Beta
2
2
 
3
- Core authentication library for Mindful Auth, designed for Astro applications.
4
-
5
- Install Mindful Auth Astro template here https://docs.mindfulauth.com/guides/frontend/astro/astro-setup/
6
-
7
- ## Features
8
-
9
- - 🔐 Session validation and management
10
- - 🛡️ Security headers (CSP, HSTS, etc.)
11
- - 🚦 Public/protected route handling
12
- - 🔄 Auth proxy for login, registration, 2FA, etc.
13
- - 🛠️ Path traversal protection
14
- - ⚡ Built for Cloudflare Workers & Astro SSR
3
+ Core authentication library for Mindful Auth, designed for Astro 6 applications.
15
4
 
16
5
  ## License
17
-
18
6
  MIT
@@ -1 +1 @@
1
- {"version":3,"file":"auth-handler.d.ts","sourceRoot":"","sources":["../src/auth-handler.ts"],"names":[],"mappings":"AAmEA,2EAA2E;AAC3E,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4BpH;AAED,gEAAgE;AAChE,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwDrH"}
1
+ {"version":3,"file":"auth-handler.d.ts","sourceRoot":"","sources":["../src/auth-handler.ts"],"names":[],"mappings":"AAwEA,2EAA2E;AAC3E,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6BpH;AAED,gEAAgE;AAChE,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyDrH"}
@@ -1,5 +1,10 @@
1
1
  // Auth proxy handler for Mindful Auth
2
2
  // Forwards authentication requests to the central Mindful Auth service
3
+ //
4
+ // ASTRO 6 MIGRATION:
5
+ // - Astro v6 removed context.locals.runtime.env. Env vars now import from 'cloudflare:workers'.
6
+ // - Note: @cloudflare/workers-types must be installed and referenced in env.d.ts.
7
+ import { env } from 'cloudflare:workers';
3
8
  import { CENTRAL_AUTH_ORIGIN, ALLOWED_AUTH_METHODS, MAX_BODY_SIZE_BYTES, AUTH_PROXY_TIMEOUT_MS } from './config';
4
9
  import { sanitizeEndpoint } from './security';
5
10
  const JSON_HEADERS = { 'Content-Type': 'application/json' };
@@ -60,7 +65,8 @@ function buildResponse(data, status, cookie) {
60
65
  }
61
66
  /** Handle GET requests (email verification, password reset links, etc.) */
62
67
  export async function handleAuthGet(rawEndpoint, request, url, locals) {
63
- const internalApiKey = locals?.runtime?.env?.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
68
+ // ASTRO 6 CHANGE: Environment variables accessed from 'cloudflare:workers' env directly.
69
+ const internalApiKey = env.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
64
70
  if (!internalApiKey)
65
71
  console.error('[auth-handler] INTERNAL_API_KEY not configured');
66
72
  const endpoint = sanitizeEndpoint(rawEndpoint);
@@ -94,7 +100,8 @@ export async function handleAuthPost(rawEndpoint, request, url, locals) {
94
100
  const contentLength = request.headers.get('content-length');
95
101
  if (contentLength && parseInt(contentLength) > MAX_BODY_SIZE_BYTES)
96
102
  return jsonError('Payload too large', 413);
97
- const internalApiKey = locals?.runtime?.env?.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
103
+ // ASTRO 6 CHANGE: Environment variables accessed from 'cloudflare:workers' env directly.
104
+ const internalApiKey = env.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
98
105
  if (!internalApiKey)
99
106
  console.error('[auth-handler] INTERNAL_API_KEY not configured');
100
107
  const endpoint = sanitizeEndpoint(rawEndpoint);
package/dist/config.d.ts CHANGED
@@ -15,6 +15,10 @@ export declare const PUBLIC_PREFIXES: string[];
15
15
  * Configure Mindful Auth security settings including CSP sources and static
16
16
  * assets to skip. Returns the options to be passed to getMauthViteDefines().
17
17
  *
18
+ * IMPORTANT: A nonce must be provided for inline styles to work. Without a nonce, inline styles will be blocked by CSP. If you need 'unsafe-inline' for development or testing, explicitly add it to styleSources in mauthSecurityConfig().
19
+ *
20
+ * IMPORTANT: Do not use Astro 6 security.csp in astro.config.mjs alongside this function. Astro's CSP will override these headers. Use mauthSecurityConfig() exclusively.
21
+ *
18
22
  * Call in astro.config.mjs and pass the result to vite.define:
19
23
  *
20
24
  * @example
@@ -25,7 +29,15 @@ export declare const PUBLIC_PREFIXES: string[];
25
29
  * scriptSources: ['https://analytics.example.com'],
26
30
  * connectSources: ['https://api.example.com'],
27
31
  * frameSources: ['https://stripe.com'],
28
- * fontSources: ['https://fonts.googleapis.com']
32
+ * fontSources: ['https://fonts.googleapis.com'],
33
+ * styleSources: ['https://cdn.example.com/styles'],
34
+ * imageSources: ['https://images.example.com'],
35
+ * frameAncestors: ["'self'"],
36
+ * workerSources: ["'self'"],
37
+ * manifestSources: ["'self'"],
38
+ * objectSources: ["'none'"],
39
+ * baseUris: ["'self'"],
40
+ * formActions: ["'self'"]
29
41
  * }
30
42
  * });
31
43
  *
@@ -40,6 +52,14 @@ export declare function mauthSecurityConfig(options?: {
40
52
  connectSources?: string[];
41
53
  frameSources?: string[];
42
54
  fontSources?: string[];
55
+ styleSources?: string[];
56
+ imageSources?: string[];
57
+ frameAncestors?: string[];
58
+ workerSources?: string[];
59
+ manifestSources?: string[];
60
+ objectSources?: string[];
61
+ baseUris?: string[];
62
+ formActions?: string[];
43
63
  };
44
64
  }): typeof options;
45
65
  /**
@@ -54,16 +74,15 @@ export declare function getMauthViteDefines(options?: {
54
74
  connectSources?: string[];
55
75
  frameSources?: string[];
56
76
  fontSources?: string[];
77
+ styleSources?: string[];
78
+ imageSources?: string[];
79
+ frameAncestors?: string[];
80
+ workerSources?: string[];
81
+ manifestSources?: string[];
82
+ objectSources?: string[];
83
+ baseUris?: string[];
84
+ formActions?: string[];
57
85
  };
58
86
  }): Record<string, string>;
59
- /**
60
- * Get security headers with CSP sources merged from defaults and build-time
61
- * custom values injected via vite.define (from getMauthViteDefines()).
62
- *
63
- * Pass the per-request `nonce` generated by `generateNonce()` so the
64
- * Content-Security-Policy includes `'nonce-<value>'` in `script-src`.
65
- * The same nonce must be set as the `nonce` attribute on every inline
66
- * `<script>` tag (available via `Astro.locals.cspNonce` in layouts).
67
- */
68
87
  export declare function GET_SECURITY_HEADERS(nonce?: string): Record<string, string>;
69
88
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AACjE,eAAO,MAAM,UAAU,gCAAgC,CAAC;AAGxD,eAAO,MAAM,oBAAoB,UAAkB,CAAC;AACpD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAC3C,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AAenD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAID,eAAO,MAAM,aAAa,UAQzB,CAAC;AAIF,eAAO,MAAM,eAAe,UAO3B,CAAC;AA0CF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE;QACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;CACL,GAAG,OAAO,OAAO,CAEjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE;QACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;CACL,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiD3E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AACjE,eAAO,MAAM,UAAU,gCAAgC,CAAC;AAGxD,eAAO,MAAM,oBAAoB,UAAkB,CAAC;AACpD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAC3C,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AAenD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAID,eAAO,MAAM,aAAa,UAQzB,CAAC;AAIF,eAAO,MAAM,eAAe,UAO3B,CAAC;AA2FF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE;QACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;CACL,GAAG,OAAO,OAAO,CAEjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,EAAE;QACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;CACL,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBzB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2E3E"}
package/dist/config.js CHANGED
@@ -74,10 +74,47 @@ const DEFAULT_FONT_SOURCES = [
74
74
  "'self'",
75
75
  'data:'
76
76
  ];
77
+ // Default allowed style sources for CSP
78
+ const DEFAULT_STYLE_SOURCES = [
79
+ "'self'",
80
+ ];
81
+ // Default allowed image sources for CSP
82
+ const DEFAULT_IMAGE_SOURCES = [
83
+ "'self'",
84
+ 'https:'
85
+ ];
86
+ // Default allowed frame ancestors for CSP (clickjacking protection)
87
+ const DEFAULT_FRAME_ANCESTORS = [
88
+ "'self'"
89
+ ];
90
+ // Default allowed worker sources for CSP (Web Workers, Service Workers)
91
+ const DEFAULT_WORKER_SOURCES = [
92
+ "'self'"
93
+ ];
94
+ // Default allowed manifest sources for CSP (web app manifest)
95
+ const DEFAULT_MANIFEST_SOURCES = [
96
+ "'self'"
97
+ ];
98
+ // Default allowed object sources for CSP (disallow plugins like Flash/Java)
99
+ const DEFAULT_OBJECT_SOURCES = [
100
+ "'none'"
101
+ ];
102
+ // Default allowed base URIs for CSP
103
+ const DEFAULT_BASE_URIS = [
104
+ "'self'"
105
+ ];
106
+ // Default allowed form actions for CSP
107
+ const DEFAULT_FORM_ACTIONS = [
108
+ "'self'"
109
+ ];
77
110
  /**
78
111
  * Configure Mindful Auth security settings including CSP sources and static
79
112
  * assets to skip. Returns the options to be passed to getMauthViteDefines().
80
113
  *
114
+ * IMPORTANT: A nonce must be provided for inline styles to work. Without a nonce, inline styles will be blocked by CSP. If you need 'unsafe-inline' for development or testing, explicitly add it to styleSources in mauthSecurityConfig().
115
+ *
116
+ * IMPORTANT: Do not use Astro 6 security.csp in astro.config.mjs alongside this function. Astro's CSP will override these headers. Use mauthSecurityConfig() exclusively.
117
+ *
81
118
  * Call in astro.config.mjs and pass the result to vite.define:
82
119
  *
83
120
  * @example
@@ -88,7 +125,15 @@ const DEFAULT_FONT_SOURCES = [
88
125
  * scriptSources: ['https://analytics.example.com'],
89
126
  * connectSources: ['https://api.example.com'],
90
127
  * frameSources: ['https://stripe.com'],
91
- * fontSources: ['https://fonts.googleapis.com']
128
+ * fontSources: ['https://fonts.googleapis.com'],
129
+ * styleSources: ['https://cdn.example.com/styles'],
130
+ * imageSources: ['https://images.example.com'],
131
+ * frameAncestors: ["'self'"],
132
+ * workerSources: ["'self'"],
133
+ * manifestSources: ["'self'"],
134
+ * objectSources: ["'none'"],
135
+ * baseUris: ["'self'"],
136
+ * formActions: ["'self'"]
92
137
  * }
93
138
  * });
94
139
  *
@@ -111,27 +156,39 @@ export function getMauthViteDefines(options) {
111
156
  __MAUTH_CONNECT_SOURCES__: JSON.stringify(options?.csp?.connectSources ?? []),
112
157
  __MAUTH_FRAME_SOURCES__: JSON.stringify(options?.csp?.frameSources ?? []),
113
158
  __MAUTH_FONT_SOURCES__: JSON.stringify(options?.csp?.fontSources ?? []),
159
+ __MAUTH_STYLE_SOURCES__: JSON.stringify(options?.csp?.styleSources ?? []),
160
+ __MAUTH_IMAGE_SOURCES__: JSON.stringify(options?.csp?.imageSources ?? []),
161
+ __MAUTH_FRAME_ANCESTORS__: JSON.stringify(options?.csp?.frameAncestors ?? []),
162
+ __MAUTH_WORKER_SOURCES__: JSON.stringify(options?.csp?.workerSources ?? []),
163
+ __MAUTH_MANIFEST_SOURCES__: JSON.stringify(options?.csp?.manifestSources ?? []),
164
+ __MAUTH_OBJECT_SOURCES__: JSON.stringify(options?.csp?.objectSources ?? []),
165
+ __MAUTH_BASE_URIS__: JSON.stringify(options?.csp?.baseUris ?? []),
166
+ __MAUTH_FORM_ACTIONS__: JSON.stringify(options?.csp?.formActions ?? []),
114
167
  };
115
168
  }
116
- /**
117
- * Get security headers with CSP sources merged from defaults and build-time
118
- * custom values injected via vite.define (from getMauthViteDefines()).
119
- *
120
- * Pass the per-request `nonce` generated by `generateNonce()` so the
121
- * Content-Security-Policy includes `'nonce-<value>'` in `script-src`.
122
- * The same nonce must be set as the `nonce` attribute on every inline
123
- * `<script>` tag (available via `Astro.locals.cspNonce` in layouts).
124
- */
125
169
  export function GET_SECURITY_HEADERS(nonce) {
126
170
  // Custom sources are baked in at build time via vite.define
127
- const SCRIPT_SOURCES = [
128
- ...DEFAULT_SCRIPT_SOURCES,
129
- ...__MAUTH_SCRIPT_SOURCES__,
130
- ...(nonce ? [`'nonce-${nonce}'`] : []),
131
- ];
132
- const CONNECT_SOURCES = [...DEFAULT_CONNECT_SOURCES, ...__MAUTH_CONNECT_SOURCES__];
133
- const FRAME_SOURCES = [...DEFAULT_FRAME_SOURCES, ...__MAUTH_FRAME_SOURCES__];
134
- const FONT_SOURCES = [...DEFAULT_FONT_SOURCES, ...__MAUTH_FONT_SOURCES__];
171
+ // Using Set to deduplicate sources while preserving order
172
+ const SCRIPT_SOURCES = [...new Set([
173
+ ...DEFAULT_SCRIPT_SOURCES,
174
+ ...__MAUTH_SCRIPT_SOURCES__,
175
+ ...(nonce ? [`'nonce-${nonce}'`] : []),
176
+ ])];
177
+ const STYLE_SOURCES = [...new Set([
178
+ ...DEFAULT_STYLE_SOURCES,
179
+ ...__MAUTH_STYLE_SOURCES__,
180
+ ...(nonce ? [`'nonce-${nonce}'`] : []),
181
+ ])];
182
+ const CONNECT_SOURCES = [...new Set([...DEFAULT_CONNECT_SOURCES, ...__MAUTH_CONNECT_SOURCES__])];
183
+ const FRAME_SOURCES = [...new Set([...DEFAULT_FRAME_SOURCES, ...__MAUTH_FRAME_SOURCES__])];
184
+ const FONT_SOURCES = [...new Set([...DEFAULT_FONT_SOURCES, ...__MAUTH_FONT_SOURCES__])];
185
+ const IMAGE_SOURCES = [...new Set([...DEFAULT_IMAGE_SOURCES, ...__MAUTH_IMAGE_SOURCES__])];
186
+ const FRAME_ANCESTORS = [...new Set([...DEFAULT_FRAME_ANCESTORS, ...__MAUTH_FRAME_ANCESTORS__])];
187
+ const WORKER_SOURCES = [...new Set([...DEFAULT_WORKER_SOURCES, ...__MAUTH_WORKER_SOURCES__])];
188
+ const MANIFEST_SOURCES = [...new Set([...DEFAULT_MANIFEST_SOURCES, ...__MAUTH_MANIFEST_SOURCES__])];
189
+ const OBJECT_SOURCES = [...new Set([...DEFAULT_OBJECT_SOURCES, ...__MAUTH_OBJECT_SOURCES__])];
190
+ const BASE_URIS = [...new Set([...DEFAULT_BASE_URIS, ...__MAUTH_BASE_URIS__])];
191
+ const FORM_ACTIONS = [...new Set([...DEFAULT_FORM_ACTIONS, ...__MAUTH_FORM_ACTIONS__])];
135
192
  return {
136
193
  'X-Content-Type-Options': 'nosniff',
137
194
  'X-Frame-Options': 'SAMEORIGIN',
@@ -139,25 +196,33 @@ export function GET_SECURITY_HEADERS(nonce) {
139
196
  'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
140
197
  'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
141
198
  'Content-Security-Policy': [
142
- // Default policy
143
- "default-src 'self'",
199
+ // Deny by default - only allow explicitly defined sources
200
+ "default-src 'none'",
144
201
  // Scripts
145
202
  `script-src ${SCRIPT_SOURCES.join(' ')}`,
146
203
  // Styles
147
- "style-src 'self' 'unsafe-inline'",
204
+ `style-src ${STYLE_SOURCES.join(' ')}`,
148
205
  // Images
149
- "img-src 'self' data: https:",
206
+ `img-src ${IMAGE_SOURCES.join(' ')}`,
150
207
  // Connections
151
208
  `connect-src ${CONNECT_SOURCES.join(' ')}`,
152
209
  // Frames
153
210
  `frame-src ${FRAME_SOURCES.join(' ')}`,
211
+ // Frame ancestors (clickjacking protection)
212
+ `frame-ancestors ${FRAME_ANCESTORS.join(' ')}`,
154
213
  // Fonts
155
214
  `font-src ${FONT_SOURCES.join(' ')}`,
215
+ // Web Workers and Service Workers
216
+ `worker-src ${WORKER_SOURCES.join(' ')}`,
217
+ // Web app manifest
218
+ `manifest-src ${MANIFEST_SOURCES.join(' ')}`,
156
219
  // Disallow object/embed tags (Flash, Java, etc.)
157
- "object-src 'none'",
220
+ `object-src ${OBJECT_SOURCES.join(' ')}`,
221
+ // Base URI
222
+ `base-uri ${BASE_URIS.join(' ')}`,
223
+ // Form action
224
+ `form-action ${FORM_ACTIONS.join(' ')}`,
158
225
  // Security directives
159
- "base-uri 'self'",
160
- "form-action 'self'",
161
226
  "upgrade-insecure-requests",
162
227
  "block-all-mixed-content"
163
228
  ].join('; ')
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export * from './config';
3
3
  export * from './auth';
4
4
  export * from './auth-handler';
5
5
  export * from './security';
6
- export { onRequest } from './middleware';
6
+ export * from './middleware';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,SAAS,CAAC;AAGxB,cAAc,UAAU,CAAC;AAGzB,cAAc,QAAQ,CAAC;AAGvB,cAAc,gBAAgB,CAAC;AAG/B,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,SAAS,CAAC;AAGxB,cAAc,UAAU,CAAC;AAGzB,cAAc,QAAQ,CAAC;AAGvB,cAAc,gBAAgB,CAAC;AAG/B,cAAc,YAAY,CAAC;AAG3B,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -9,5 +9,5 @@ export * from './auth';
9
9
  export * from './auth-handler';
10
10
  // Security utilities
11
11
  export * from './security';
12
- // Middleware (also available via '@mindful-auth/core/middleware')
13
- export { onRequest } from './middleware';
12
+ // Middleware
13
+ export * from './middleware';
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,SAAS,mCAmEpB,CAAC"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,SAAS,mCA0FpB,CAAC"}
@@ -1,47 +1,80 @@
1
1
  // Global middleware for session validation
2
2
  // Runs before all route handlers to enforce authentication
3
+ //
4
+ // ASTRO 6 MIGRATION:
5
+ // - Astro v6 removed context.locals.runtime.env. Env vars now import from 'cloudflare:workers'.
6
+ // - cloudflare:workers is marked external in astro.config.vite.ssr to avoid esbuild scan errors.
7
+ // - Dev mode bypass uses import.meta.env.DEV (build-time constant: true in dev, false in prod).
3
8
  import { defineMiddleware } from 'astro:middleware';
9
+ import { env } from 'cloudflare:workers';
4
10
  import { PUBLIC_ROUTES, PUBLIC_PREFIXES, GET_SECURITY_HEADERS, GET_SKIP_ASSETS } from './config';
5
11
  import { sanitizePath, generateNonce } from './security';
6
12
  import { validateSession, validateMemberIdInUrl } from './auth';
7
13
  /** Check if a path is a public route (no auth required) */
8
14
  function isPublicRoute(pathname) {
9
15
  return PUBLIC_ROUTES.includes(pathname) ||
10
- PUBLIC_PREFIXES.some(prefix => pathname.startsWith(prefix));
16
+ PUBLIC_PREFIXES.some((prefix) => pathname.startsWith(prefix));
11
17
  }
12
- /** Add security headers (including CSP nonce) to a response */
18
+ /** Add security headers to a response */
19
+ // NOTE: In Cloudflare Workers, Response.headers is immutable.
20
+ // We must create a new Response with a fresh Headers object instead of mutating.
13
21
  function addSecurityHeaders(response, nonce) {
22
+ const newHeaders = new Headers(response.headers);
14
23
  Object.entries(GET_SECURITY_HEADERS(nonce)).forEach(([key, value]) => {
15
- response.headers.set(key, value);
24
+ newHeaders.set(key, value);
25
+ });
26
+ return new Response(response.body, {
27
+ status: response.status,
28
+ statusText: response.statusText,
29
+ headers: newHeaders,
16
30
  });
17
- return response;
18
31
  }
19
32
  // Main middleware function
20
33
  export const onRequest = defineMiddleware(async (context, next) => {
21
34
  const { request, url, redirect, locals } = context;
22
35
  const pathname = url.pathname;
23
- // Type assertion for locals (users should extend App.Locals in their project)
24
- const authLocals = locals;
36
+ // Redirect HTTP to HTTPS (skip in dev mode and localhost)
37
+ if (!import.meta.env.DEV && url.protocol === 'http:' && url.hostname !== 'localhost' && url.hostname !== '127.0.0.1') {
38
+ const httpsUrl = url.toString().replace('http://', 'https://');
39
+ return redirect(httpsUrl, 307);
40
+ }
25
41
  // Generate a per-request nonce for CSP. Exposed as locals.cspNonce so
26
42
  // layouts can add nonce={Astro.locals.cspNonce} to inline <script> tags.
27
43
  const nonce = generateNonce();
28
- authLocals.cspNonce = nonce;
44
+ locals.cspNonce = nonce;
29
45
  // Skip middleware for static assets FIRST (before dev mode)
30
46
  if (GET_SKIP_ASSETS().includes(pathname)) {
31
47
  return next();
32
48
  }
33
- // DEV MODE: Skip auth on localhost
34
- if (import.meta.env.DEV && (url.hostname === 'localhost' || url.hostname === '127.0.0.1')) {
49
+ // DEV MODE: Skip auth
50
+ // import.meta.env.DEV is a build-time constant replaced by Vite:
51
+ // - true during local dev (never included in prod build)
52
+ // - false in production builds (tree-shaken out entirely)
53
+ // Localhost auth is skipped because Mindful Auth blocks localhost requests.
54
+ if (import.meta.env.DEV) {
55
+ // Check if public route first
35
56
  if (isPublicRoute(pathname)) {
36
- authLocals.recordId = null;
57
+ locals.recordId = null;
37
58
  return next();
38
59
  }
39
60
  // For protected routes, extract or create mock recordId
40
61
  // Match memberid with trailing slash
41
62
  const match = pathname.match(/^\/([a-zA-Z0-9-]+)(?:\/|$)/);
42
- authLocals.recordId = match ? match[1] : 'dev-user-123';
63
+ locals.recordId = match ? match[1] : 'dev-user-123';
43
64
  return next();
44
65
  }
66
+ // PREVIEW MODE: Skip auth (for testing without Mindful Auth on localhost)
67
+ // npm run preview builds the project first, so import.meta.env.DEV is false.
68
+ // We need a runtime check for localhost to simulate auth in preview.
69
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
70
+ if (isPublicRoute(pathname)) {
71
+ locals.recordId = null;
72
+ return addSecurityHeaders(await next(), nonce);
73
+ }
74
+ const match = pathname.match(/^\/([a-zA-Z0-9-]+)(?:\/|$)/);
75
+ locals.recordId = match ? match[1] : 'preview-user-123';
76
+ return addSecurityHeaders(await next(), nonce);
77
+ }
45
78
  // Sanitize path
46
79
  const safePath = sanitizePath(pathname);
47
80
  if (!safePath) {
@@ -49,11 +82,13 @@ export const onRequest = defineMiddleware(async (context, next) => {
49
82
  }
50
83
  // Public routes - no auth needed
51
84
  if (isPublicRoute(safePath)) {
52
- authLocals.recordId = null;
85
+ locals.recordId = null;
53
86
  return addSecurityHeaders(await next(), nonce);
54
87
  }
55
88
  // Protected route - validate session
56
- const internalApiKey = authLocals.runtime?.env?.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
89
+ // ASTRO 6 CHANGE: Environment variables are accessed directly from 'cloudflare:workers' env,
90
+ // not from context.locals.runtime.env (which was removed).
91
+ const internalApiKey = env.INTERNAL_API_KEY || import.meta.env.INTERNAL_API_KEY;
57
92
  if (!internalApiKey) {
58
93
  console.error('[middleware] CRITICAL: INTERNAL_API_KEY not configured');
59
94
  return new Response('Internal Server Error', { status: 500 });
@@ -71,6 +106,6 @@ export const onRequest = defineMiddleware(async (context, next) => {
71
106
  if (!validateMemberIdInUrl(safePath, session.recordId).valid) {
72
107
  return new Response('Forbidden: Invalid user ID in URL', { status: 403 });
73
108
  }
74
- authLocals.recordId = session.recordId;
109
+ locals.recordId = session.recordId;
75
110
  return addSecurityHeaders(await next(), nonce);
76
111
  });
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { MiddlewareHandler } from 'astro';
2
2
  export interface MindfulAuthLocals {
3
3
  recordId: string | null;
4
- cspNonce?: string;
4
+ cspNonce: string;
5
5
  runtime?: {
6
6
  env?: {
7
7
  INTERNAL_API_KEY?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAG/C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE;YACJ,gBAAgB,CAAC,EAAE,MAAM,CAAC;SAC3B,CAAC;KACH,CAAC;CACH;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,MAAO,SAAQ,iBAAiB;SAAG;KAC9C;CACF;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAG/C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE;YACJ,gBAAgB,CAAC,EAAE,MAAM,CAAC;SAC3B,CAAC;KACH,CAAC;CACH;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,MAAO,SAAQ,iBAAiB;SAAG;KAC9C;CACF;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAC"}
package/dist/types.js CHANGED
@@ -1,2 +1,3 @@
1
1
  // Type definitions for Mindful Auth
2
+ /// <reference types="@cloudflare/workers-types" />
2
3
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mindfulauth/core",
3
- "version": "1.2.1",
4
- "description": "Mindful Auth core authentication library for Astro",
3
+ "version": "2.0.0-beta.3",
4
+ "description": "Mindful Auth core authentication library for Astro 6",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -42,10 +42,11 @@
42
42
  "author": "Mindful Auth",
43
43
  "license": "MIT",
44
44
  "peerDependencies": {
45
- "astro": "^5.0.0"
45
+ "astro": "^6.0.0-beta.14"
46
46
  },
47
47
  "devDependencies": {
48
- "astro": "^5.17.3",
48
+ "@cloudflare/workers-types": "^4.20260303.0",
49
+ "astro": "^6.0.0-beta.15",
49
50
  "typescript": "^5.9.3"
50
51
  }
51
- }
52
+ }