@growth-labs/auth 0.10.2 → 0.10.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,sBAAsB,CAAA;AAQ7B,wBAAsB,SAAS,CAC9B,OAAO,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACxE,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAoGnB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,sBAAsB,CAAA;AAc7B,wBAAsB,SAAS,CAC9B,OAAO,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACxE,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAsGnB"}
@@ -5,6 +5,11 @@ import { getCookie, setTokenCookies } from '../utils/cookies.js';
5
5
  import { secretResolutionFailureResponse } from '../utils/fail-closed.js';
6
6
  import { resolveInternalIssuer } from '../utils/issuer.js';
7
7
  import { refreshTokens, verifyAccessToken } from '../utils/tokens.js';
8
+ const PRIVATE_NO_STORE_HEADERS = [
9
+ 'Cache-Control',
10
+ 'CDN-Cache-Control',
11
+ 'Cloudflare-CDN-Cache-Control',
12
+ ];
8
13
  export async function onRequest(context, next) {
9
14
  const options = getConfig();
10
15
  const issuer = resolveInternalIssuer(options);
@@ -81,7 +86,9 @@ export async function onRequest(context, next) {
81
86
  setTokenCookies(modifiedResponse, options, refreshed.accessToken, refreshed.refreshToken);
82
87
  }
83
88
  if (context.locals.synthetic === true) {
84
- modifiedResponse.headers.set('Cache-Control', 'no-store');
89
+ for (const header of PRIVATE_NO_STORE_HEADERS) {
90
+ modifiedResponse.headers.set(header, 'private, no-store');
91
+ }
85
92
  }
86
93
  return modifiedResponse;
87
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,14 @@ import type { AuthRendererBrand, AuthRendererResult, ForgotPasswordRendererPaylo
9
9
  * `{ module, export }` spec rather than a function anyway.
10
10
  */
11
11
  export declare function getRenderer<K extends keyof ResolvedAuthRenderers>(surface: K): ResolvedAuthRenderers[K] | undefined;
12
+ /**
13
+ * Materialize an auth renderer result into a `Response` and — for HTML — stamp a
14
+ * self-contained `<meta http-equiv="Content-Security-Policy">` covering the
15
+ * page's inline scripts/styles. See `utils/csp.ts` for why the auth surfaces
16
+ * cannot rely on Astro's native `security.csp` pass (they are raw endpoint
17
+ * responses, not component-rendered pages). A renderer's own Response status and
18
+ * headers are preserved exactly; only the body is rewritten to carry the CSP.
19
+ */
12
20
  export declare function renderHtmlResponse(result: AuthRendererResult): Promise<Response>;
13
21
  export declare function rendererBrand(options: {
14
22
  loginPage: {
@@ -1 +1 @@
1
- {"version":3,"file":"renderers.d.ts","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderers.d.ts","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"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"}
package/dist/renderers.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { renderers as bundledRenderers } from 'virtual:growth-labs/auth/renderers';
2
+ import { injectAuthCsp } from './utils/csp.js';
2
3
  /**
3
4
  * Look up the bundled renderer for a surface. After 0.3.7 the lookup is
4
5
  * build-time only — renderers are resolved into the virtual module by the
@@ -11,12 +12,36 @@ import { renderers as bundledRenderers } from 'virtual:growth-labs/auth/renderer
11
12
  export function getRenderer(surface) {
12
13
  return bundledRenderers[surface];
13
14
  }
15
+ /**
16
+ * Detect an HTML response from its `Content-Type`. Auth renderers that return a
17
+ * bare string are always HTML; a renderer returning a `Response` may set any
18
+ * content type (e.g. an error `Response`), so only text/html bodies get the CSP.
19
+ */
20
+ function isHtmlContentType(contentType) {
21
+ return (contentType ?? '').toLowerCase().includes('text/html');
22
+ }
23
+ /**
24
+ * Materialize an auth renderer result into a `Response` and — for HTML — stamp a
25
+ * self-contained `<meta http-equiv="Content-Security-Policy">` covering the
26
+ * page's inline scripts/styles. See `utils/csp.ts` for why the auth surfaces
27
+ * cannot rely on Astro's native `security.csp` pass (they are raw endpoint
28
+ * responses, not component-rendered pages). A renderer's own Response status and
29
+ * headers are preserved exactly; only the body is rewritten to carry the CSP.
30
+ */
14
31
  export async function renderHtmlResponse(result) {
15
32
  const rendered = await result;
16
33
  if (rendered instanceof Response) {
17
- return rendered;
34
+ if (!isHtmlContentType(rendered.headers.get('content-type'))) {
35
+ return rendered;
36
+ }
37
+ const html = await rendered.text();
38
+ return new Response(await injectAuthCsp(html), {
39
+ status: rendered.status,
40
+ statusText: rendered.statusText,
41
+ headers: rendered.headers,
42
+ });
18
43
  }
19
- return new Response(rendered, {
44
+ return new Response(await injectAuthCsp(rendered), {
20
45
  headers: { 'Content-Type': 'text/html; charset=utf-8' },
21
46
  });
22
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"renderers.js","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderers.js","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"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"}
@@ -37,10 +37,10 @@ export async function GET(context) {
37
37
  if (renderer) {
38
38
  return renderHtmlResponse(renderer(loginRendererPayload(options, data)));
39
39
  }
40
- const html = renderLoginPage(options, data);
41
- return new Response(html, {
42
- headers: { 'Content-Type': 'text/html; charset=utf-8' },
43
- });
40
+ // The built-in fallback emits an inline <script> and <style>; route it through
41
+ // renderHtmlResponse so it gets the same self-contained CSP meta as a consumer
42
+ // renderer would (see utils/csp.ts).
43
+ return renderHtmlResponse(renderLoginPage(options, data));
44
44
  }
45
45
  function renderLoginPage(options, data) {
46
46
  const { loginPage, providers } = options;
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/routes/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/routes/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Self-contained Content-Security-Policy for auth renderer pages.
3
+ *
4
+ * WHY THIS EXISTS. The auth integration serves its UI surfaces (`/login`,
5
+ * `/verify-code`, `/password`, `/forgot-password`, `/reset-password`) from
6
+ * `injectRoute`-registered Astro endpoints that return a raw `Response` built
7
+ * from an HTML string. Astro's native hash-based `security.csp` only rewrites
8
+ * *component-rendered* pages — it scans the rendered document, hashes inline
9
+ * `<script>`/`<style>`, and injects a `<meta http-equiv>` CSP. An endpoint that
10
+ * returns its own `Response` bypasses that pass entirely, so on a site with an
11
+ * enforced `security.csp` the auth pages ship transport headers but NO CSP and
12
+ * their inline theme-bootstrap script is un-hashed (fulcrum-labs/warfronts
13
+ * verified live: `/login` 200, no `Content-Security-Policy` header or meta).
14
+ *
15
+ * This is the auth-side analogue of the `@growth-labs/consent@0.7.1`
16
+ * `context.csp.insertScriptHash` fix. Consent registers its banner hash and
17
+ * lets Astro render the CSP meta into the `.astro` page; auth has no Astro page
18
+ * to carry a meta, so it computes the sha256 hashes of its own inline scripts
19
+ * and styles and emits the `<meta http-equiv="Content-Security-Policy">` itself.
20
+ * Browsers combine a CSP meta with any CSP response header by intersection, so a
21
+ * site keeps owning the transport-header CSP (or none) while auth owns the meta
22
+ * on its own surfaces — exactly the model warfronts documents (CSP lives in the
23
+ * meta on SSR pages; the transport middleware never emits a CSP header).
24
+ */
25
+ /** A sha256 CSP source expression, e.g. `sha256-<base64>`. */
26
+ export type CspHash = `sha256-${string}`;
27
+ /**
28
+ * Compute the CSP sha256 hash of an inline `<script>`/`<style>` body. Hashes the
29
+ * exact bytes the browser hashes — the raw text between the element's open and
30
+ * close tags, with no trimming — so the emitted `'sha256-…'` matches what the
31
+ * browser recomputes for the served content. Mirrors the consent package's
32
+ * `sha256CspHash` (SHA-256 → base64) so the two packages stay wire-compatible.
33
+ */
34
+ export declare function sha256CspHash(content: string): Promise<CspHash>;
35
+ /** Inner text of every inline `<script>` (no `src=`) and `<style>` element. */
36
+ interface InlineSources {
37
+ scripts: string[];
38
+ styles: string[];
39
+ }
40
+ /**
41
+ * Extract the inner bodies of inline `<script>` and `<style>` elements. A
42
+ * `<script src=…>` reference has no hashable body (its bytes live at another
43
+ * URL, covered by `script-src 'self'`) and is skipped. Empty bodies are skipped
44
+ * — an empty inline script needs no hash. De-duplicates identical bodies so an
45
+ * element repeated on a page contributes a single hash.
46
+ */
47
+ export declare function extractInlineSources(html: string): InlineSources;
48
+ /** True when the document already carries a CSP `<meta http-equiv>` tag. */
49
+ export declare function hasCspMeta(html: string): boolean;
50
+ /**
51
+ * Build the CSP policy string for an auth page from its inline-source hashes.
52
+ *
53
+ * - `default-src 'self'` is the floor; anything not otherwise named falls here.
54
+ * - `script-src 'self' <hashes>` allows same-origin scripts plus the exact
55
+ * inline scripts on the page. No `'unsafe-inline'`: every inline script is
56
+ * hashed, so the policy stays strict.
57
+ * - `style-src 'self' <hashes>` does the same for inline `<style>` blocks.
58
+ * - `style-src-attr 'unsafe-inline'` permits inline `style="…"` attributes,
59
+ * which are NOT hashable (Astro's own CSP takes the same allowance for
60
+ * attribute styles). Element styles remain hash-pinned.
61
+ * - `img-src 'self' data: https:` covers the brand logo and inline data URIs.
62
+ * - `connect-src 'self'` bounds fetch/XHR (the code-request/verify POSTs are
63
+ * same-origin). `form-action 'self'` bounds the login/verify form posts.
64
+ * - `object-src 'none'`, `base-uri 'self'`, `frame-ancestors 'none'` are the
65
+ * standard hardening trio (no plugins, no base-tag hijack, no framing).
66
+ */
67
+ export declare function buildAuthCspPolicy(sources: {
68
+ scriptHashes: CspHash[];
69
+ styleHashes: CspHash[];
70
+ }): string;
71
+ /**
72
+ * Compute the auth CSP for `html` and return the document with a
73
+ * `<meta http-equiv="Content-Security-Policy">` inserted at the top of `<head>`.
74
+ *
75
+ * Idempotent: if the document already carries a CSP meta (a consumer renderer
76
+ * that emits its own), the input is returned unchanged so we never emit two
77
+ * conflicting policies (browsers would intersect them and likely break the
78
+ * page). Every inline `<script>`/`<style>` the page ships is hash-pinned, so the
79
+ * emitted policy is strict — no `'unsafe-inline'` for scripts or element styles.
80
+ */
81
+ export declare function injectAuthCsp(html: string): Promise<string>;
82
+ export {};
83
+ //# sourceMappingURL=csp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csp.d.ts","sourceRoot":"","sources":["../../src/utils/csp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,8DAA8D;AAC9D,MAAM,MAAM,OAAO,GAAG,UAAU,MAAM,EAAE,CAAA;AAExC;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIrE;AAED,+EAA+E;AAC/E,UAAU,aAAa;IACtB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,MAAM,EAAE,CAAA;CAChB;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAmBhE;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC3C,YAAY,EAAE,OAAO,EAAE,CAAA;IACvB,WAAW,EAAE,OAAO,EAAE,CAAA;CACtB,GAAG,MAAM,CAeT;AA6BD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQjE"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Self-contained Content-Security-Policy for auth renderer pages.
3
+ *
4
+ * WHY THIS EXISTS. The auth integration serves its UI surfaces (`/login`,
5
+ * `/verify-code`, `/password`, `/forgot-password`, `/reset-password`) from
6
+ * `injectRoute`-registered Astro endpoints that return a raw `Response` built
7
+ * from an HTML string. Astro's native hash-based `security.csp` only rewrites
8
+ * *component-rendered* pages — it scans the rendered document, hashes inline
9
+ * `<script>`/`<style>`, and injects a `<meta http-equiv>` CSP. An endpoint that
10
+ * returns its own `Response` bypasses that pass entirely, so on a site with an
11
+ * enforced `security.csp` the auth pages ship transport headers but NO CSP and
12
+ * their inline theme-bootstrap script is un-hashed (fulcrum-labs/warfronts
13
+ * verified live: `/login` 200, no `Content-Security-Policy` header or meta).
14
+ *
15
+ * This is the auth-side analogue of the `@growth-labs/consent@0.7.1`
16
+ * `context.csp.insertScriptHash` fix. Consent registers its banner hash and
17
+ * lets Astro render the CSP meta into the `.astro` page; auth has no Astro page
18
+ * to carry a meta, so it computes the sha256 hashes of its own inline scripts
19
+ * and styles and emits the `<meta http-equiv="Content-Security-Policy">` itself.
20
+ * Browsers combine a CSP meta with any CSP response header by intersection, so a
21
+ * site keeps owning the transport-header CSP (or none) while auth owns the meta
22
+ * on its own surfaces — exactly the model warfronts documents (CSP lives in the
23
+ * meta on SSR pages; the transport middleware never emits a CSP header).
24
+ */
25
+ /**
26
+ * Compute the CSP sha256 hash of an inline `<script>`/`<style>` body. Hashes the
27
+ * exact bytes the browser hashes — the raw text between the element's open and
28
+ * close tags, with no trimming — so the emitted `'sha256-…'` matches what the
29
+ * browser recomputes for the served content. Mirrors the consent package's
30
+ * `sha256CspHash` (SHA-256 → base64) so the two packages stay wire-compatible.
31
+ */
32
+ export async function sha256CspHash(content) {
33
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(content));
34
+ const base64 = btoa(String.fromCharCode(...new Uint8Array(digest)));
35
+ return `sha256-${base64}`;
36
+ }
37
+ const INLINE_SCRIPT_RE = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
38
+ const INLINE_STYLE_RE = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi;
39
+ const HAS_SRC_RE = /\bsrc\s*=/i;
40
+ /**
41
+ * Extract the inner bodies of inline `<script>` and `<style>` elements. A
42
+ * `<script src=…>` reference has no hashable body (its bytes live at another
43
+ * URL, covered by `script-src 'self'`) and is skipped. Empty bodies are skipped
44
+ * — an empty inline script needs no hash. De-duplicates identical bodies so an
45
+ * element repeated on a page contributes a single hash.
46
+ */
47
+ export function extractInlineSources(html) {
48
+ const scripts = new Set();
49
+ const styles = new Set();
50
+ for (const match of html.matchAll(INLINE_SCRIPT_RE)) {
51
+ const attrs = match[1] ?? '';
52
+ const body = match[2] ?? '';
53
+ if (HAS_SRC_RE.test(attrs))
54
+ continue;
55
+ if (body.length === 0)
56
+ continue;
57
+ scripts.add(body);
58
+ }
59
+ for (const match of html.matchAll(INLINE_STYLE_RE)) {
60
+ const body = match[1] ?? '';
61
+ if (body.length === 0)
62
+ continue;
63
+ styles.add(body);
64
+ }
65
+ return { scripts: [...scripts], styles: [...styles] };
66
+ }
67
+ /** True when the document already carries a CSP `<meta http-equiv>` tag. */
68
+ export function hasCspMeta(html) {
69
+ return /<meta\b[^>]*\bhttp-equiv\s*=\s*["']?content-security-policy["']?/i.test(html);
70
+ }
71
+ /**
72
+ * Build the CSP policy string for an auth page from its inline-source hashes.
73
+ *
74
+ * - `default-src 'self'` is the floor; anything not otherwise named falls here.
75
+ * - `script-src 'self' <hashes>` allows same-origin scripts plus the exact
76
+ * inline scripts on the page. No `'unsafe-inline'`: every inline script is
77
+ * hashed, so the policy stays strict.
78
+ * - `style-src 'self' <hashes>` does the same for inline `<style>` blocks.
79
+ * - `style-src-attr 'unsafe-inline'` permits inline `style="…"` attributes,
80
+ * which are NOT hashable (Astro's own CSP takes the same allowance for
81
+ * attribute styles). Element styles remain hash-pinned.
82
+ * - `img-src 'self' data: https:` covers the brand logo and inline data URIs.
83
+ * - `connect-src 'self'` bounds fetch/XHR (the code-request/verify POSTs are
84
+ * same-origin). `form-action 'self'` bounds the login/verify form posts.
85
+ * - `object-src 'none'`, `base-uri 'self'`, `frame-ancestors 'none'` are the
86
+ * standard hardening trio (no plugins, no base-tag hijack, no framing).
87
+ */
88
+ export function buildAuthCspPolicy(sources) {
89
+ const scriptSrc = ["script-src 'self'", ...sources.scriptHashes.map((h) => `'${h}'`)].join(' ');
90
+ const styleSrc = ["style-src 'self'", ...sources.styleHashes.map((h) => `'${h}'`)].join(' ');
91
+ return [
92
+ "default-src 'self'",
93
+ scriptSrc,
94
+ styleSrc,
95
+ "style-src-attr 'unsafe-inline'",
96
+ "img-src 'self' data: https:",
97
+ "connect-src 'self'",
98
+ "form-action 'self'",
99
+ "object-src 'none'",
100
+ "base-uri 'self'",
101
+ "frame-ancestors 'none'",
102
+ ].join('; ');
103
+ }
104
+ function escapeHtmlAttribute(value) {
105
+ return value
106
+ .replace(/&/g, '&amp;')
107
+ .replace(/"/g, '&quot;')
108
+ .replace(/'/g, '&#39;')
109
+ .replace(/</g, '&lt;')
110
+ .replace(/>/g, '&gt;');
111
+ }
112
+ /**
113
+ * Insert `tag` immediately after the opening `<head>` (or, absent a head, after
114
+ * `<html>`, or as a prefix) so the policy is parsed before any inline content.
115
+ */
116
+ function injectIntoHead(html, tag) {
117
+ const headOpen = /<head\b[^>]*>/i.exec(html);
118
+ if (headOpen) {
119
+ const at = headOpen.index + headOpen[0].length;
120
+ return `${html.slice(0, at)}${tag}${html.slice(at)}`;
121
+ }
122
+ const htmlOpen = /<html\b[^>]*>/i.exec(html);
123
+ if (htmlOpen) {
124
+ const at = htmlOpen.index + htmlOpen[0].length;
125
+ return `${html.slice(0, at)}${tag}${html.slice(at)}`;
126
+ }
127
+ return `${tag}${html}`;
128
+ }
129
+ /**
130
+ * Compute the auth CSP for `html` and return the document with a
131
+ * `<meta http-equiv="Content-Security-Policy">` inserted at the top of `<head>`.
132
+ *
133
+ * Idempotent: if the document already carries a CSP meta (a consumer renderer
134
+ * that emits its own), the input is returned unchanged so we never emit two
135
+ * conflicting policies (browsers would intersect them and likely break the
136
+ * page). Every inline `<script>`/`<style>` the page ships is hash-pinned, so the
137
+ * emitted policy is strict — no `'unsafe-inline'` for scripts or element styles.
138
+ */
139
+ export async function injectAuthCsp(html) {
140
+ if (hasCspMeta(html))
141
+ return html;
142
+ const { scripts, styles } = extractInlineSources(html);
143
+ const scriptHashes = await Promise.all(scripts.map(sha256CspHash));
144
+ const styleHashes = await Promise.all(styles.map(sha256CspHash));
145
+ const policy = buildAuthCspPolicy({ scriptHashes, styleHashes });
146
+ const tag = `<meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(policy)}">`;
147
+ return injectIntoHead(html, tag);
148
+ }
149
+ //# sourceMappingURL=csp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csp.js","sourceRoot":"","sources":["../../src/utils/csp.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export { type AuthAnalyticsProvider, type AuthFailureEventInput, type AuthSuccessEventInput, authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
2
  export type { ClientSecretLiteral, ClientSecretResolutionErrorCode, ClientSecretResult, } from './client-secret.js';
3
3
  export { isClientSecretLiteral, literalClientSecret } from './client-secret.js';
4
+ export { buildAuthCspPolicy, type CspHash, extractInlineSources, hasCspMeta, injectAuthCsp, sha256CspHash, } from './csp.js';
4
5
  export { ClientSecretResolutionError } from './fail-closed.js';
5
6
  export { type BridgeMigrationOptions, type BridgeMigrationStatement, buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
6
7
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACX,mBAAmB,EACnB,+BAA+B,EAC/B,kBAAkB,GAClB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACX,mBAAmB,EACnB,+BAA+B,EAC/B,kBAAkB,GAClB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EACN,kBAAkB,EAClB,KAAK,OAAO,EACZ,oBAAoB,EACpB,UAAU,EACV,aAAa,EACb,aAAa,GACb,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -1,5 +1,6 @@
1
1
  export { authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
2
  export { isClientSecretLiteral, literalClientSecret } from './client-secret.js';
3
+ export { buildAuthCspPolicy, extractInlineSources, hasCspMeta, injectAuthCsp, sha256CspHash, } from './csp.js';
3
4
  export { ClientSecretResolutionError } from './fail-closed.js';
4
5
  export { buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
5
6
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AAMvB,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EAEvB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AAMvB,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EACN,kBAAkB,EAElB,oBAAoB,EACpB,UAAU,EACV,aAAa,EACb,aAAa,GACb,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EAEvB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/auth",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -57,7 +57,7 @@
57
57
  "typescript": "^5.8.0",
58
58
  "vite": "^7.3.5",
59
59
  "vitest": "^4.1.8",
60
- "@growth-labs/analytics": "0.14.7"
60
+ "@growth-labs/analytics": "0.16.1"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsc",
@@ -7,6 +7,12 @@ import { secretResolutionFailureResponse } from '../utils/fail-closed.js'
7
7
  import { resolveInternalIssuer } from '../utils/issuer.js'
8
8
  import { refreshTokens, verifyAccessToken } from '../utils/tokens.js'
9
9
 
10
+ const PRIVATE_NO_STORE_HEADERS = [
11
+ 'Cache-Control',
12
+ 'CDN-Cache-Control',
13
+ 'Cloudflare-CDN-Cache-Control',
14
+ ] as const
15
+
10
16
  export async function onRequest(
11
17
  context: { request: Request; url: URL; locals: Record<string, unknown> },
12
18
  next: () => Promise<Response>,
@@ -104,7 +110,9 @@ export async function onRequest(
104
110
  setTokenCookies(modifiedResponse, options, refreshed.accessToken, refreshed.refreshToken)
105
111
  }
106
112
  if (context.locals.synthetic === true) {
107
- modifiedResponse.headers.set('Cache-Control', 'no-store')
113
+ for (const header of PRIVATE_NO_STORE_HEADERS) {
114
+ modifiedResponse.headers.set(header, 'private, no-store')
115
+ }
108
116
  }
109
117
  return modifiedResponse
110
118
  }
package/src/renderers.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  ResolvedAuthRenderers,
10
10
  VerifyCodeRendererPayload,
11
11
  } from './types.js'
12
+ import { injectAuthCsp } from './utils/csp.js'
12
13
 
13
14
  /**
14
15
  * Look up the bundled renderer for a surface. After 0.3.7 the lookup is
@@ -25,12 +26,37 @@ export function getRenderer<K extends keyof ResolvedAuthRenderers>(
25
26
  return bundledRenderers[surface]
26
27
  }
27
28
 
29
+ /**
30
+ * Detect an HTML response from its `Content-Type`. Auth renderers that return a
31
+ * bare string are always HTML; a renderer returning a `Response` may set any
32
+ * content type (e.g. an error `Response`), so only text/html bodies get the CSP.
33
+ */
34
+ function isHtmlContentType(contentType: string | null): boolean {
35
+ return (contentType ?? '').toLowerCase().includes('text/html')
36
+ }
37
+
38
+ /**
39
+ * Materialize an auth renderer result into a `Response` and — for HTML — stamp a
40
+ * self-contained `<meta http-equiv="Content-Security-Policy">` covering the
41
+ * page's inline scripts/styles. See `utils/csp.ts` for why the auth surfaces
42
+ * cannot rely on Astro's native `security.csp` pass (they are raw endpoint
43
+ * responses, not component-rendered pages). A renderer's own Response status and
44
+ * headers are preserved exactly; only the body is rewritten to carry the CSP.
45
+ */
28
46
  export async function renderHtmlResponse(result: AuthRendererResult): Promise<Response> {
29
47
  const rendered = await result
30
48
  if (rendered instanceof Response) {
31
- return rendered
49
+ if (!isHtmlContentType(rendered.headers.get('content-type'))) {
50
+ return rendered
51
+ }
52
+ const html = await rendered.text()
53
+ return new Response(await injectAuthCsp(html), {
54
+ status: rendered.status,
55
+ statusText: rendered.statusText,
56
+ headers: rendered.headers,
57
+ })
32
58
  }
33
- return new Response(rendered, {
59
+ return new Response(await injectAuthCsp(rendered), {
34
60
  headers: { 'Content-Type': 'text/html; charset=utf-8' },
35
61
  })
36
62
  }
@@ -56,10 +56,10 @@ export async function GET(context: LoginContext): Promise<Response> {
56
56
  return renderHtmlResponse(renderer(loginRendererPayload(options, data)))
57
57
  }
58
58
 
59
- const html = renderLoginPage(options, data)
60
- return new Response(html, {
61
- headers: { 'Content-Type': 'text/html; charset=utf-8' },
62
- })
59
+ // The built-in fallback emits an inline <script> and <style>; route it through
60
+ // renderHtmlResponse so it gets the same self-contained CSP meta as a consumer
61
+ // renderer would (see utils/csp.ts).
62
+ return renderHtmlResponse(renderLoginPage(options, data))
63
63
  }
64
64
 
65
65
  interface LoginPageData {
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Self-contained Content-Security-Policy for auth renderer pages.
3
+ *
4
+ * WHY THIS EXISTS. The auth integration serves its UI surfaces (`/login`,
5
+ * `/verify-code`, `/password`, `/forgot-password`, `/reset-password`) from
6
+ * `injectRoute`-registered Astro endpoints that return a raw `Response` built
7
+ * from an HTML string. Astro's native hash-based `security.csp` only rewrites
8
+ * *component-rendered* pages — it scans the rendered document, hashes inline
9
+ * `<script>`/`<style>`, and injects a `<meta http-equiv>` CSP. An endpoint that
10
+ * returns its own `Response` bypasses that pass entirely, so on a site with an
11
+ * enforced `security.csp` the auth pages ship transport headers but NO CSP and
12
+ * their inline theme-bootstrap script is un-hashed (fulcrum-labs/warfronts
13
+ * verified live: `/login` 200, no `Content-Security-Policy` header or meta).
14
+ *
15
+ * This is the auth-side analogue of the `@growth-labs/consent@0.7.1`
16
+ * `context.csp.insertScriptHash` fix. Consent registers its banner hash and
17
+ * lets Astro render the CSP meta into the `.astro` page; auth has no Astro page
18
+ * to carry a meta, so it computes the sha256 hashes of its own inline scripts
19
+ * and styles and emits the `<meta http-equiv="Content-Security-Policy">` itself.
20
+ * Browsers combine a CSP meta with any CSP response header by intersection, so a
21
+ * site keeps owning the transport-header CSP (or none) while auth owns the meta
22
+ * on its own surfaces — exactly the model warfronts documents (CSP lives in the
23
+ * meta on SSR pages; the transport middleware never emits a CSP header).
24
+ */
25
+
26
+ /** A sha256 CSP source expression, e.g. `sha256-<base64>`. */
27
+ export type CspHash = `sha256-${string}`
28
+
29
+ /**
30
+ * Compute the CSP sha256 hash of an inline `<script>`/`<style>` body. Hashes the
31
+ * exact bytes the browser hashes — the raw text between the element's open and
32
+ * close tags, with no trimming — so the emitted `'sha256-…'` matches what the
33
+ * browser recomputes for the served content. Mirrors the consent package's
34
+ * `sha256CspHash` (SHA-256 → base64) so the two packages stay wire-compatible.
35
+ */
36
+ export async function sha256CspHash(content: string): Promise<CspHash> {
37
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(content))
38
+ const base64 = btoa(String.fromCharCode(...new Uint8Array(digest)))
39
+ return `sha256-${base64}`
40
+ }
41
+
42
+ /** Inner text of every inline `<script>` (no `src=`) and `<style>` element. */
43
+ interface InlineSources {
44
+ scripts: string[]
45
+ styles: string[]
46
+ }
47
+
48
+ const INLINE_SCRIPT_RE = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi
49
+ const INLINE_STYLE_RE = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi
50
+ const HAS_SRC_RE = /\bsrc\s*=/i
51
+
52
+ /**
53
+ * Extract the inner bodies of inline `<script>` and `<style>` elements. A
54
+ * `<script src=…>` reference has no hashable body (its bytes live at another
55
+ * URL, covered by `script-src 'self'`) and is skipped. Empty bodies are skipped
56
+ * — an empty inline script needs no hash. De-duplicates identical bodies so an
57
+ * element repeated on a page contributes a single hash.
58
+ */
59
+ export function extractInlineSources(html: string): InlineSources {
60
+ const scripts = new Set<string>()
61
+ const styles = new Set<string>()
62
+
63
+ for (const match of html.matchAll(INLINE_SCRIPT_RE)) {
64
+ const attrs = match[1] ?? ''
65
+ const body = match[2] ?? ''
66
+ if (HAS_SRC_RE.test(attrs)) continue
67
+ if (body.length === 0) continue
68
+ scripts.add(body)
69
+ }
70
+
71
+ for (const match of html.matchAll(INLINE_STYLE_RE)) {
72
+ const body = match[1] ?? ''
73
+ if (body.length === 0) continue
74
+ styles.add(body)
75
+ }
76
+
77
+ return { scripts: [...scripts], styles: [...styles] }
78
+ }
79
+
80
+ /** True when the document already carries a CSP `<meta http-equiv>` tag. */
81
+ export function hasCspMeta(html: string): boolean {
82
+ return /<meta\b[^>]*\bhttp-equiv\s*=\s*["']?content-security-policy["']?/i.test(html)
83
+ }
84
+
85
+ /**
86
+ * Build the CSP policy string for an auth page from its inline-source hashes.
87
+ *
88
+ * - `default-src 'self'` is the floor; anything not otherwise named falls here.
89
+ * - `script-src 'self' <hashes>` allows same-origin scripts plus the exact
90
+ * inline scripts on the page. No `'unsafe-inline'`: every inline script is
91
+ * hashed, so the policy stays strict.
92
+ * - `style-src 'self' <hashes>` does the same for inline `<style>` blocks.
93
+ * - `style-src-attr 'unsafe-inline'` permits inline `style="…"` attributes,
94
+ * which are NOT hashable (Astro's own CSP takes the same allowance for
95
+ * attribute styles). Element styles remain hash-pinned.
96
+ * - `img-src 'self' data: https:` covers the brand logo and inline data URIs.
97
+ * - `connect-src 'self'` bounds fetch/XHR (the code-request/verify POSTs are
98
+ * same-origin). `form-action 'self'` bounds the login/verify form posts.
99
+ * - `object-src 'none'`, `base-uri 'self'`, `frame-ancestors 'none'` are the
100
+ * standard hardening trio (no plugins, no base-tag hijack, no framing).
101
+ */
102
+ export function buildAuthCspPolicy(sources: {
103
+ scriptHashes: CspHash[]
104
+ styleHashes: CspHash[]
105
+ }): string {
106
+ const scriptSrc = ["script-src 'self'", ...sources.scriptHashes.map((h) => `'${h}'`)].join(' ')
107
+ const styleSrc = ["style-src 'self'", ...sources.styleHashes.map((h) => `'${h}'`)].join(' ')
108
+ return [
109
+ "default-src 'self'",
110
+ scriptSrc,
111
+ styleSrc,
112
+ "style-src-attr 'unsafe-inline'",
113
+ "img-src 'self' data: https:",
114
+ "connect-src 'self'",
115
+ "form-action 'self'",
116
+ "object-src 'none'",
117
+ "base-uri 'self'",
118
+ "frame-ancestors 'none'",
119
+ ].join('; ')
120
+ }
121
+
122
+ function escapeHtmlAttribute(value: string): string {
123
+ return value
124
+ .replace(/&/g, '&amp;')
125
+ .replace(/"/g, '&quot;')
126
+ .replace(/'/g, '&#39;')
127
+ .replace(/</g, '&lt;')
128
+ .replace(/>/g, '&gt;')
129
+ }
130
+
131
+ /**
132
+ * Insert `tag` immediately after the opening `<head>` (or, absent a head, after
133
+ * `<html>`, or as a prefix) so the policy is parsed before any inline content.
134
+ */
135
+ function injectIntoHead(html: string, tag: string): string {
136
+ const headOpen = /<head\b[^>]*>/i.exec(html)
137
+ if (headOpen) {
138
+ const at = headOpen.index + headOpen[0].length
139
+ return `${html.slice(0, at)}${tag}${html.slice(at)}`
140
+ }
141
+ const htmlOpen = /<html\b[^>]*>/i.exec(html)
142
+ if (htmlOpen) {
143
+ const at = htmlOpen.index + htmlOpen[0].length
144
+ return `${html.slice(0, at)}${tag}${html.slice(at)}`
145
+ }
146
+ return `${tag}${html}`
147
+ }
148
+
149
+ /**
150
+ * Compute the auth CSP for `html` and return the document with a
151
+ * `<meta http-equiv="Content-Security-Policy">` inserted at the top of `<head>`.
152
+ *
153
+ * Idempotent: if the document already carries a CSP meta (a consumer renderer
154
+ * that emits its own), the input is returned unchanged so we never emit two
155
+ * conflicting policies (browsers would intersect them and likely break the
156
+ * page). Every inline `<script>`/`<style>` the page ships is hash-pinned, so the
157
+ * emitted policy is strict — no `'unsafe-inline'` for scripts or element styles.
158
+ */
159
+ export async function injectAuthCsp(html: string): Promise<string> {
160
+ if (hasCspMeta(html)) return html
161
+ const { scripts, styles } = extractInlineSources(html)
162
+ const scriptHashes = await Promise.all(scripts.map(sha256CspHash))
163
+ const styleHashes = await Promise.all(styles.map(sha256CspHash))
164
+ const policy = buildAuthCspPolicy({ scriptHashes, styleHashes })
165
+ const tag = `<meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(policy)}">`
166
+ return injectIntoHead(html, tag)
167
+ }
@@ -16,6 +16,14 @@ export type {
16
16
  ClientSecretResult,
17
17
  } from './client-secret.js'
18
18
  export { isClientSecretLiteral, literalClientSecret } from './client-secret.js'
19
+ export {
20
+ buildAuthCspPolicy,
21
+ type CspHash,
22
+ extractInlineSources,
23
+ hasCspMeta,
24
+ injectAuthCsp,
25
+ sha256CspHash,
26
+ } from './csp.js'
19
27
  export { ClientSecretResolutionError } from './fail-closed.js'
20
28
  export {
21
29
  type BridgeMigrationOptions,