@drawnagency/primitives 0.1.53 → 0.1.55

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.
@@ -2,7 +2,10 @@ import type { Session, CookieLike } from "./types";
2
2
  export declare const SESSION_COOKIE = "bp-session";
3
3
  export declare const AUDIENCE_COOKIE = "bp-audience";
4
4
  export declare const SESSION_MAX_AGE_SECONDS: number;
5
+ export declare const AUDIENCE_MAX_AGE_SECONDS: number;
5
6
  export declare function signSessionToken(session: Session): Promise<string>;
6
7
  export declare function verifySessionToken(token: string): Promise<Session | null>;
8
+ export declare function signAudienceToken(audience: string): Promise<string>;
9
+ export declare function verifyAudienceToken(token: string): Promise<string | null>;
7
10
  export declare function setSessionCookie(cookies: CookieLike, token: string, isProduction: boolean): void;
8
11
  //# sourceMappingURL=cookies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/auth/cookies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGnD,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,uBAAuB,QAAe,CAAC;AAEpD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAWxE;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAe/E;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,OAAO,GACpB,IAAI,CAQN"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/auth/cookies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGnD,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,uBAAuB,QAAe,CAAC;AACpD,eAAO,MAAM,wBAAwB,QAAoB,CAAC;AAE1D,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAWxE;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAe/E;AAMD,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzE;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAS/E;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,OAAO,GACpB,IAAI,CAQN"}
@@ -1,5 +1,5 @@
1
1
  export type { Role, Session, SiteUser, Audience, SignInMethod, SignInResult, TokenExchangeType, TokenExchangeResult, AuthProvider, CookieLike, AuthContext, } from "./types";
2
2
  export { requireSessionSecret, isSameOriginRequest, safeNextPath } from "./security";
3
- export { signSessionToken, verifySessionToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS } from "./cookies";
3
+ export { signSessionToken, verifySessionToken, signAudienceToken, verifyAudienceToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS, AUDIENCE_MAX_AGE_SECONDS } from "./cookies";
4
4
  export { LastOwnerError } from "./errors";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAC7I,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAC/M,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  AUDIENCE_COOKIE,
3
+ AUDIENCE_MAX_AGE_SECONDS,
3
4
  LastOwnerError,
4
5
  SESSION_COOKIE,
5
6
  SESSION_MAX_AGE_SECONDS,
6
7
  setSessionCookie,
8
+ signAudienceToken,
7
9
  signSessionToken,
10
+ verifyAudienceToken,
8
11
  verifySessionToken
9
- } from "../chunk-YEOGH3QH.js";
12
+ } from "../chunk-B5VYSTPB.js";
10
13
  import {
11
14
  isSameOriginRequest,
12
15
  requireSessionSecret,
@@ -15,6 +18,7 @@ import {
15
18
  import "../chunk-WKZ7OENC.js";
16
19
  export {
17
20
  AUDIENCE_COOKIE,
21
+ AUDIENCE_MAX_AGE_SECONDS,
18
22
  LastOwnerError,
19
23
  SESSION_COOKIE,
20
24
  SESSION_MAX_AGE_SECONDS,
@@ -22,6 +26,8 @@ export {
22
26
  requireSessionSecret,
23
27
  safeNextPath,
24
28
  setSessionCookie,
29
+ signAudienceToken,
25
30
  signSessionToken,
31
+ verifyAudienceToken,
26
32
  verifySessionToken
27
33
  };
@@ -7,6 +7,7 @@ import * as jose from "jose";
7
7
  var SESSION_COOKIE = "bp-session";
8
8
  var AUDIENCE_COOKIE = "bp-audience";
9
9
  var SESSION_MAX_AGE_SECONDS = 60 * 60 * 24;
10
+ var AUDIENCE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
10
11
  async function signSessionToken(session) {
11
12
  return new jose.SignJWT({
12
13
  userId: session.userId,
@@ -18,7 +19,7 @@ async function signSessionToken(session) {
18
19
  async function verifySessionToken(token) {
19
20
  const secret = requireSessionSecret();
20
21
  try {
21
- const { payload } = await jose.jwtVerify(token, secret);
22
+ const { payload } = await jose.jwtVerify(token, secret, { algorithms: ["HS256"] });
22
23
  const role = payload.role;
23
24
  if (role !== "owner" && role !== "editor") return null;
24
25
  return {
@@ -31,6 +32,19 @@ async function verifySessionToken(token) {
31
32
  return null;
32
33
  }
33
34
  }
35
+ async function signAudienceToken(audience) {
36
+ return new jose.SignJWT({ audience }).setProtectedHeader({ alg: "HS256" }).setExpirationTime(`${AUDIENCE_MAX_AGE_SECONDS}s`).setIssuedAt().sign(requireSessionSecret());
37
+ }
38
+ async function verifyAudienceToken(token) {
39
+ const secret = requireSessionSecret();
40
+ try {
41
+ const { payload } = await jose.jwtVerify(token, secret, { algorithms: ["HS256"] });
42
+ const audience = payload.audience;
43
+ return typeof audience === "string" && audience !== "" ? audience : null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
34
48
  function setSessionCookie(cookies, token, isProduction) {
35
49
  cookies.set(SESSION_COOKIE, token, {
36
50
  httpOnly: true,
@@ -53,8 +67,11 @@ export {
53
67
  SESSION_COOKIE,
54
68
  AUDIENCE_COOKIE,
55
69
  SESSION_MAX_AGE_SECONDS,
70
+ AUDIENCE_MAX_AGE_SECONDS,
56
71
  signSessionToken,
57
72
  verifySessionToken,
73
+ signAudienceToken,
74
+ verifyAudienceToken,
58
75
  setSessionCookie,
59
76
  LastOwnerError
60
77
  };
package/dist/index.js CHANGED
@@ -61,13 +61,16 @@ import {
61
61
  } from "./chunk-24SUF2BC.js";
62
62
  import {
63
63
  AUDIENCE_COOKIE,
64
+ AUDIENCE_MAX_AGE_SECONDS,
64
65
  LastOwnerError,
65
66
  SESSION_COOKIE,
66
67
  SESSION_MAX_AGE_SECONDS,
67
68
  setSessionCookie,
69
+ signAudienceToken,
68
70
  signSessionToken,
71
+ verifyAudienceToken,
69
72
  verifySessionToken
70
- } from "./chunk-YEOGH3QH.js";
73
+ } from "./chunk-B5VYSTPB.js";
71
74
  import {
72
75
  isSameOriginRequest,
73
76
  requireSessionSecret,
@@ -100,6 +103,7 @@ import {
100
103
  } from "./chunk-DKOUFIP6.js";
101
104
  export {
102
105
  AUDIENCE_COOKIE,
106
+ AUDIENCE_MAX_AGE_SECONDS,
103
107
  AudienceColorSchema,
104
108
  AudienceNameSchema,
105
109
  AudienceSchema,
@@ -177,8 +181,10 @@ export {
177
181
  sanitizeMediaName,
178
182
  setMediaProvider,
179
183
  setSessionCookie,
184
+ signAudienceToken,
180
185
  signSessionToken,
181
186
  slugifyAudienceName,
182
187
  toSectionId,
188
+ verifyAudienceToken,
183
189
  verifySessionToken
184
190
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -5,6 +5,7 @@ import { requireSessionSecret } from "./security";
5
5
  export const SESSION_COOKIE = "bp-session";
6
6
  export const AUDIENCE_COOKIE = "bp-audience";
7
7
  export const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24;
8
+ export const AUDIENCE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
8
9
 
9
10
  export async function signSessionToken(session: Session): Promise<string> {
10
11
  return new jose.SignJWT({
@@ -22,7 +23,7 @@ export async function signSessionToken(session: Session): Promise<string> {
22
23
  export async function verifySessionToken(token: string): Promise<Session | null> {
23
24
  const secret = requireSessionSecret(); // throws on misconfiguration, before try
24
25
  try {
25
- const { payload } = await jose.jwtVerify(token, secret);
26
+ const { payload } = await jose.jwtVerify(token, secret, { algorithms: ["HS256"] });
26
27
  const role = payload.role;
27
28
  if (role !== "owner" && role !== "editor") return null;
28
29
  return {
@@ -36,6 +37,29 @@ export async function verifySessionToken(token: string): Promise<Session | null>
36
37
  }
37
38
  }
38
39
 
40
+ // The audience cookie must be unforgeable: middleware trusts its value to gate
41
+ // viewer content, so it carries a signed JWT, not a plaintext audience name.
42
+ // SESSION_SECRET is generated per-site by the provisioner, so the signature
43
+ // alone also prevents cross-site replay — no siteId claim needed.
44
+ export async function signAudienceToken(audience: string): Promise<string> {
45
+ return new jose.SignJWT({ audience })
46
+ .setProtectedHeader({ alg: "HS256" })
47
+ .setExpirationTime(`${AUDIENCE_MAX_AGE_SECONDS}s`)
48
+ .setIssuedAt()
49
+ .sign(requireSessionSecret());
50
+ }
51
+
52
+ export async function verifyAudienceToken(token: string): Promise<string | null> {
53
+ const secret = requireSessionSecret(); // throws on misconfiguration, before try
54
+ try {
55
+ const { payload } = await jose.jwtVerify(token, secret, { algorithms: ["HS256"] });
56
+ const audience = payload.audience;
57
+ return typeof audience === "string" && audience !== "" ? audience : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+
39
63
  export function setSessionCookie(
40
64
  cookies: CookieLike,
41
65
  token: string,
package/src/auth/index.ts CHANGED
@@ -13,5 +13,5 @@ export type {
13
13
  } from "./types";
14
14
 
15
15
  export { requireSessionSecret, isSameOriginRequest, safeNextPath } from "./security";
16
- export { signSessionToken, verifySessionToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS } from "./cookies";
16
+ export { signSessionToken, verifySessionToken, signAudienceToken, verifyAudienceToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS, AUDIENCE_MAX_AGE_SECONDS } from "./cookies";
17
17
  export { LastOwnerError } from "./errors";
@@ -444,7 +444,7 @@ export default function EditorShell({
444
444
  if (restoredContent) {
445
445
  return {
446
446
  section: { id, ...restoredContent },
447
- meta: restoredIndex.sections[id] ?? existing?.meta ?? { type: restoredContent.type, status: "draft", access: ["internal"] },
447
+ meta: restoredIndex.sections[id] ?? existing?.meta ?? { type: restoredContent.type, status: "draft", access: [] },
448
448
  };
449
449
  }
450
450
  if (existing) {