CSP Configuration (Astro 6)
Astro 6 includes native Content Security Policy support via security.csp in astro.config.mjs. Mindful Auth provides utilities to generate the correct script hashes automatically so you don’t have to maintain them manually.
Imports
Section titled “Imports”Two modules from @mindfulauth/astro are needed for CSP configuration:
import { mauthSecurityConfig, getMauthViteDefines } from '@mindfulauth/astro/config';import { getScriptHashes, getScriptHashesFromDir } from '@mindfulauth/astro/csp';mauthSecurityConfig— configures which asset paths skip session validationgetMauthViteDefines— passes the security config to Vite at build timegetScriptHashes()— returnssha384-hashes for all built-in Mindful Auth auth scriptsgetScriptHashesFromDir(path)— returnssha384-hashes for inline scripts in a custom component directory
Required Domains
Section titled “Required Domains”Your CSP must allow connections to these external origins:
| Directive | Domain | Purpose |
|---|---|---|
script-src | https://api.mindfulauth.com | Mindful Auth API scripts |
script-src / frame-src | https://challenges.cloudflare.com | Cloudflare Turnstile |
connect-src | https://api.mindfulauth.com | Mindful Auth API requests |
Script Hash Utilities
Section titled “Script Hash Utilities”Mindful Auth’s auth scripts use inline <script> tags, which CSP blocks by default. The @mindfulauth/astro/csp module generates sha384- hashes automatically at build time.
getScriptHashes()
Section titled “getScriptHashes()”Returns sha384- hashes for all auth scripts bundled in @mindfulauth/astro. Always include this.
getScriptHashesFromDir(dirPath)
Section titled “getScriptHashesFromDir(dirPath)”Returns sha384- hashes for inline scripts found in a directory of .astro component files. The template uses this for src/components/ to cover any custom inline scripts you add there.
Full Configuration Example
Section titled “Full Configuration Example”import { defineConfig } from 'astro/config';import cloudflare from '@astrojs/cloudflare';import { fileURLToPath } from 'url';import { mauthSecurityConfig, getMauthViteDefines } from '@mindfulauth/astro/config';import { getScriptHashes, getScriptHashesFromDir } from '@mindfulauth/astro/csp';
// Configure which asset paths skip session validationconst mauthCfg = mauthSecurityConfig({ // SECURITY CRITICAL: Only add actual static file paths here. // NEVER add application routes — this bypasses authentication entirely. skipAssets: [ // Example: '/sitemap.xml', // Example: '/manifest.webmanifest', ],});
export default defineConfig({ output: 'server',
adapter: cloudflare({ routes: { // Serve these as static assets without going through SSR middleware include: ['/favicon.ico', '/robots.txt', '/.well-known/'], exclude: [], } }),
vite: { define: getMauthViteDefines(mauthCfg), ssr: { external: ['cloudflare:workers'] } },
security: { checkOrigin: true, allowedDomains: [ { hostname: 'mindfulauth.com', protocol: 'https' } ], // Maximum body size for action requests (1 MB for auth flows) actionBodySizeLimit: 1048576,
csp: { // Use SHA-384 for stronger hash security algorithm: 'SHA-384',
scriptDirective: { resources: [ "'self'", 'https://api.mindfulauth.com', 'https://challenges.cloudflare.com', 'https://*.cloudflareinsights.com', ], // Auto-computed at build time: // getScriptHashes() covers @mindfulauth/astro auth scripts // getScriptHashesFromDir() covers custom components in src/components/ hashes: [ ...getScriptHashes(), ...getScriptHashesFromDir(fileURLToPath(new URL('./src/components', import.meta.url))), ], },
styleDirective: { // Astro auto-injects hashes for bundled styles resources: ["'self'"] },
// Additional directives beyond script-src and style-src directives: [ "default-src 'none'", "connect-src 'self' https://api.mindfulauth.com https://challenges.cloudflare.com", "frame-src 'self' https://challenges.cloudflare.com", "frame-ancestors 'none'", "font-src 'self' data:", "img-src 'self' https: data:", "media-src 'self' https:", "worker-src 'self' blob:", "manifest-src 'self'", "object-src 'none'", "base-uri 'self'", "form-action 'self'", "require-trusted-types-for 'script'", "upgrade-insecure-requests", ], } }});Important Notes
Section titled “Important Notes”algorithm: 'SHA-384': Astro 6’s CSP uses SHA-384 for stronger hash security. Mindful Auth’sgetScriptHashes()returns hashes with the matching algorithm.styleDirective: Astro automatically injects hashes for its bundled styles —"'self'"is sufficient in most cases.getScriptHashesFromDir(): The template scanssrc/components/by default. Only add directories that contain stable inline scripts.skipAssets: Only add genuine static file paths (e.g./sitemap.xml). Never add authenticated routes — this bypasses session validation entirely.- Third-party scripts: Adding a domain to
scriptDirective.resourcesallows the script file itself to load, but if that script injects its own inline<script>tags at runtime, those will still be blocked by CSP. In those cases, adding the domain alone is not enough — you may need a custom integration or wrapper that avoids inline script injection, or use the middleware workaround above to relax CSP on specific routes.
Known Limitation: 'unsafe-inline' Is Ignored When algorithm Is Set
Section titled “Known Limitation: 'unsafe-inline' Is Ignored When algorithm Is Set”If you have specific routes that need to allow runtime inline styles, you can work around this with a middleware that strips the auto-generated style hashes from the Content-Security-Policy response header on those routes — allowing 'unsafe-inline' to take effect there while keeping the full strict CSP everywhere else.
import { sequence } from 'astro:middleware';import { onRequest as mauthMiddleware } from '@mindfulauth/astro/middleware';import type { MiddlewareHandler } from 'astro';
// Strips auto-injected style hashes on routes that need runtime inline styles.// Remove this once Astro ships styleDirective.hashes: false.const stripStyleHashesOnSpecificRoutes: MiddlewareHandler = async (ctx, next) => { const response = await next();
// Only apply to the routes that need it if (!ctx.url.pathname.match(/^\/your-route-pattern\//)) return response; if (!response.headers.get('content-type')?.includes('text/html')) return response;
const csp = response.headers.get('content-security-policy'); if (!csp) return response;
const patched = csp.replace( /style-src\s[^;]*/g, (match) => match.replace(/\s*'sha(?:256|384|512)-[A-Za-z0-9+/=]+'/g, ''), );
const newHeaders = new Headers(response.headers); newHeaders.set('content-security-policy', patched); return new Response(response.body, { status: response.status, headers: newHeaders });};
// The Mindful Auth middleware MUST remain first in the sequenceexport const onRequest = sequence(mauthMiddleware, stripStyleHashesOnSpecificRoutes);Only patch the routes that genuinely require it. All other routes will continue to use the full strict CSP from your config.