@aurostack/stacks 0.2.1 → 0.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurostack/stacks",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Scaffold new projects from house templates: a NestJS API, React app or Turborepo monorepo, and Node or Python workers. Subtractive templates plus a zero-dependency generator.",
5
5
  "license": "MIT",
6
6
  "author": "Aurostack (https://github.com/aurostack-org)",
@@ -8,7 +8,10 @@ APP_HOST='0.0.0.0'
8
8
  PORT='5000'
9
9
  SERVER_HOST='http://localhost:5000'
10
10
  FRONTEND_HOST='http://localhost:3000'
11
- # Extra CORS origins beyond FRONTEND_HOST, comma separated.
11
+ # Extra origins beyond FRONTEND_HOST, comma separated: trusted by auth in every
12
+ # environment, allowed by CORS in development only. A full origin
13
+ # (`http://localhost:3001`) is used as written; a bare hostname (`example.com`)
14
+ # expands to https://example.com and its auth., app. and admin. subdomains.
12
15
  MISC_CORS_ORIGINS=''
13
16
 
14
17
  DATABASE_URL='postgresql://root:postgres@localhost:5432/main'
@@ -16,9 +19,13 @@ DATABASE_URL='postgresql://root:postgres@localhost:5432/main'
16
19
  # openssl rand -base64 32
17
20
  BETTER_AUTH_SECRET='change-me-openssl-rand-base64-32'
18
21
  BETTER_AUTH_URL='http://localhost:5000'
19
- # Leading dot shares the session cookie across subdomains. `.lvh.me` resolves
20
- # to 127.0.0.1 for every subdomain, which makes local multi-app auth work.
21
- BETTER_AUTH_COOKIE_DOMAIN='.lvh.me'
22
+ # Empty: the session cookie belongs to the API's own host, which is what
23
+ # localhost development and a single-origin frontend need. Set a parent domain
24
+ # with a leading dot (`.example.com`) when frontends live on subdomains, to share
25
+ # one login across them; locally, `.lvh.me` does the same with `*.lvh.me` hosts
26
+ # (every subdomain resolves to 127.0.0.1). The domain must cover the host the
27
+ # browser talks to, or the cookie is dropped and every sign-in bounces.
28
+ BETTER_AUTH_COOKIE_DOMAIN=''
22
29
  BETTER_AUTH_COOKIE_PREFIX='acme'
23
30
 
24
31
  # Seeded by `yarn db:seed`.
@@ -81,13 +81,15 @@ import { OriginBuilder } from 'common/misc';
81
81
  ipAddressHeaders: config.rateLimit.ipHeaders
82
82
  },
83
83
  // @feature:end
84
- // Share the session cookie across subdomains so one login covers
85
- // every frontend. Domain comes from BETTER_AUTH_COOKIE_DOMAIN
86
- // (`.lvh.me` in dev, your apex in production).
84
+ // With BETTER_AUTH_COOKIE_DOMAIN set (`.example.com`), the session
85
+ // cookie is shared across subdomains so one login covers every
86
+ // frontend. Empty, it belongs to the API's own host: right for
87
+ // localhost and for a single-origin frontend. A browser drops a
88
+ // cookie whose domain doesn't cover the host that set it.
87
89
  cookiePrefix: config.betterAuth.cookiePrefix,
88
90
  crossSubDomainCookies: {
89
- enabled: true,
90
- domain: config.betterAuth.cookieDomain
91
+ enabled: Boolean(config.betterAuth.cookieDomain),
92
+ domain: config.betterAuth.cookieDomain || undefined
91
93
  },
92
94
  database: {
93
95
  joins: true
@@ -0,0 +1,24 @@
1
+ import { OriginBuilder } from 'common/misc/origin-builder';
2
+
3
+ describe('OriginBuilder', () => {
4
+ it('expands a bare hostname into https origins for it and its subdomains', () => {
5
+ expect(OriginBuilder.build('example.com')).toEqual([
6
+ 'https://example.com',
7
+ 'https://auth.example.com',
8
+ 'https://app.example.com',
9
+ 'https://admin.example.com'
10
+ ]);
11
+ });
12
+
13
+ it('takes an entry with a scheme as one origin, as written', () => {
14
+ expect(OriginBuilder.build('http://localhost:3001, http://localhost:3002/')).toEqual([
15
+ 'http://localhost:3001',
16
+ 'http://localhost:3002'
17
+ ]);
18
+ });
19
+
20
+ it('is empty for an empty list', () => {
21
+ expect(OriginBuilder.build('')).toEqual([]);
22
+ expect(OriginBuilder.build(undefined)).toEqual([]);
23
+ });
24
+ });
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Builds a list of origins for CORS configuration, given a list of hostnames.
3
- * Each hostname will generate the main domain and subdomains
4
- * for app, auth, and admin.
2
+ * Builds a list of origins for CORS configuration from a list of entries.
3
+ * A bare hostname (`shop.example.com`) generates https origins for it and its
4
+ * app, auth and admin subdomains. An entry with a scheme
5
+ * (`http://localhost:3001`) is taken as one origin, as written: that is how
6
+ * frontends on other local ports are allowed.
5
7
  *
6
8
  * @remarks This is mainly for development purposes
7
9
  * For frontend developers on the team who cannot run a standalone
@@ -28,6 +30,7 @@ export class OriginBuilder {
28
30
  * Origins for a single hostname: the main domain plus each subdomain.
29
31
  */
30
32
  fromHost(hostname: string): string[] {
33
+ if (hostname.includes('://')) return [hostname.replace(/\/+$/, '')];
31
34
  return [
32
35
  `${this.protocol}${hostname}`,
33
36
  ...this.subdomains.map((sub) => `${this.protocol}${sub}.${hostname}`)
@@ -172,7 +172,7 @@ namespace Config {
172
172
  DATABASE_URL: str(),
173
173
  BETTER_AUTH_SECRET: str(),
174
174
  BETTER_AUTH_URL: str(),
175
- BETTER_AUTH_COOKIE_DOMAIN: str(),
175
+ BETTER_AUTH_COOKIE_DOMAIN: z.string().default(''),
176
176
  BETTER_AUTH_COOKIE_PREFIX: str(),
177
177
  SUPERUSER_EMAIL: str(),
178
178
  SUPERUSER_PASSWORD: str(),
@@ -33,8 +33,8 @@ export const auth = betterAuth({
33
33
  // sessions through this one, so a different cookie name would reject them.
34
34
  cookiePrefix: env.BETTER_AUTH_COOKIE_PREFIX,
35
35
  crossSubDomainCookies: {
36
- enabled: true,
37
- domain: env.BETTER_AUTH_COOKIE_DOMAIN
36
+ enabled: Boolean(env.BETTER_AUTH_COOKIE_DOMAIN),
37
+ domain: env.BETTER_AUTH_COOKIE_DOMAIN || undefined
38
38
  },
39
39
  database: {
40
40
  joins: true