- API (api/src/auth.ts): scrypt password hashes; random session tokens stored only as SHA-256; 30-day sliding sessions; rate limits on sign-in (per IP and per email) and sign-up; changing the password signs out other devices; deleting the account removes its data. - Site: /signup, /signin (returns to the page the reader came from), /account; header link; plain forms, no JavaScript needed. Session in an HTTP-only, SameSite=Lax cookie (Secure over HTTPS). - CSRF: Astro's origin check, with the site's hostnames listed (SITE_HOSTS) so its own form posts pass and other sites' are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
17 lines
655 B
JavaScript
17 lines
655 B
JavaScript
import { defineConfig } from 'astro/config';
|
|
import node from '@astrojs/node';
|
|
|
|
// Server-rendered pages (content comes from the Divan API at request time)
|
|
export default defineConfig({
|
|
output: 'server',
|
|
adapter: node({ mode: 'standalone' }),
|
|
server: { port: 4200 },
|
|
// Form posts from other sites are refused (CSRF). Astro only trusts the Host header for these
|
|
// hostnames, so the production domain must be listed (SITE_HOSTS at build time, comma-separated).
|
|
security: {
|
|
checkOrigin: true,
|
|
allowedDomains: (process.env.SITE_HOSTS ?? '127.0.0.1,localhost').split(',').map((h) => ({ hostname: h.trim() })),
|
|
},
|
|
i18n: undefined,
|
|
});
|