@poly-x/next 0.1.0-alpha.17 → 0.1.0-alpha.19

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/dist/server.cjs CHANGED
@@ -131,6 +131,20 @@ function geoHeadersFromBody(geo) {
131
131
  if (g.consent === "granted" || g.consent === "denied" || g.consent === "revoked") headers["x-polyx-geo-consent"] = g.consent;
132
132
  return headers;
133
133
  }
134
+ /**
135
+ * Build the device-context headers (`x-timezone` / `x-screen-resolution`) from the browser's login
136
+ * body (v6). These are not automatic request headers — the browser captures them client-side and
137
+ * posts them; the platform reads them for session device attribution. Only well-formed values are
138
+ * forwarded (a sane timezone string, a `WxH` resolution) so junk never reaches the platform.
139
+ */
140
+ function deviceContextHeadersFromBody(deviceContext) {
141
+ const headers = {};
142
+ if (!deviceContext || typeof deviceContext !== "object") return headers;
143
+ const d = deviceContext;
144
+ if (typeof d.timezone === "string" && d.timezone.length > 0 && d.timezone.length <= 64) headers["x-timezone"] = d.timezone;
145
+ if (typeof d.screenResolution === "string" && /^\d{1,5}x\d{1,5}$/.test(d.screenResolution)) headers["x-screen-resolution"] = d.screenResolution;
146
+ return headers;
147
+ }
134
148
  /** Read the current session — safe in Server Components (no cookie write). */
135
149
  async function auth(overrides) {
136
150
  const config = resolveNextConfig(overrides);
@@ -188,6 +202,7 @@ function createAuthHandlers(handlersConfig = {}) {
188
202
  return Response.json({ status: "invalid_request" }, { status: 400 });
189
203
  }
190
204
  const geoHeaders = geoHeadersFromBody(payload.geo);
205
+ const deviceContextHeaders = deviceContextHeadersFromBody(payload.deviceContext);
191
206
  const email = typeof payload.email === "string" ? payload.email : "";
192
207
  const password = typeof payload.password === "string" ? payload.password : "";
193
208
  if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
@@ -211,7 +226,8 @@ function createAuthHandlers(handlersConfig = {}) {
211
226
  case "authorized":
212
227
  await newSession(adapt(await (0, next_headers.cookies)()), config, secure).establishFromCode(outcome.code, verifier, redirectUri, {
213
228
  ...collectForwardedDeviceHeaders(request),
214
- ...geoHeaders
229
+ ...geoHeaders,
230
+ ...deviceContextHeaders
215
231
  });
216
232
  return Response.json({
217
233
  status: "success",
@@ -341,6 +357,19 @@ function createAuthHandlers(handlersConfig = {}) {
341
357
  },
342
358
  async session() {
343
359
  return Response.json(await auth(handlersConfig));
360
+ },
361
+ async subscriptionCredential() {
362
+ const config = resolveNextConfig(handlersConfig);
363
+ const current = await new require_server_session.ServerSession({
364
+ authClient: buildServerAuthClient(config),
365
+ secret: config.secret,
366
+ cookies: await requestCookies()
367
+ }).read();
368
+ if (!current) return Response.json({ status: "signed_out" }, { status: 401 });
369
+ return Response.json({
370
+ token: current.tokens.accessToken,
371
+ organizationId: current.session.claims.organizationId
372
+ });
344
373
  }
345
374
  };
346
375
  }
package/dist/server.d.cts CHANGED
@@ -127,6 +127,14 @@ interface AuthHandlers {
127
127
  * server truth instead. Never carries a token.
128
128
  */
129
129
  session(request: Request): Promise<Response>;
130
+ /**
131
+ * v2 (ADR-0008): a session-derived credential for the real-time `authz:changed` channel. Returns
132
+ * `{ token, organizationId }` read from the sealed session server-side — the token is for the
133
+ * socket handshake only (in-memory; never persisted), per the pragmatic custody decision. A
134
+ * signed-out caller gets `401` with no token. This is the one place a token leaves the BFF, and
135
+ * only to open the live-authz subscription.
136
+ */
137
+ subscriptionCredential(request: Request): Promise<Response>;
130
138
  }
131
139
  /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
132
140
  type ForgotPasswordResult = {
package/dist/server.d.mts CHANGED
@@ -127,6 +127,14 @@ interface AuthHandlers {
127
127
  * server truth instead. Never carries a token.
128
128
  */
129
129
  session(request: Request): Promise<Response>;
130
+ /**
131
+ * v2 (ADR-0008): a session-derived credential for the real-time `authz:changed` channel. Returns
132
+ * `{ token, organizationId }` read from the sealed session server-side — the token is for the
133
+ * socket handshake only (in-memory; never persisted), per the pragmatic custody decision. A
134
+ * signed-out caller gets `401` with no token. This is the one place a token leaves the BFF, and
135
+ * only to open the live-authz subscription.
136
+ */
137
+ subscriptionCredential(request: Request): Promise<Response>;
130
138
  }
131
139
  /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
132
140
  type ForgotPasswordResult = {
package/dist/server.mjs CHANGED
@@ -130,6 +130,20 @@ function geoHeadersFromBody(geo) {
130
130
  if (g.consent === "granted" || g.consent === "denied" || g.consent === "revoked") headers["x-polyx-geo-consent"] = g.consent;
131
131
  return headers;
132
132
  }
133
+ /**
134
+ * Build the device-context headers (`x-timezone` / `x-screen-resolution`) from the browser's login
135
+ * body (v6). These are not automatic request headers — the browser captures them client-side and
136
+ * posts them; the platform reads them for session device attribution. Only well-formed values are
137
+ * forwarded (a sane timezone string, a `WxH` resolution) so junk never reaches the platform.
138
+ */
139
+ function deviceContextHeadersFromBody(deviceContext) {
140
+ const headers = {};
141
+ if (!deviceContext || typeof deviceContext !== "object") return headers;
142
+ const d = deviceContext;
143
+ if (typeof d.timezone === "string" && d.timezone.length > 0 && d.timezone.length <= 64) headers["x-timezone"] = d.timezone;
144
+ if (typeof d.screenResolution === "string" && /^\d{1,5}x\d{1,5}$/.test(d.screenResolution)) headers["x-screen-resolution"] = d.screenResolution;
145
+ return headers;
146
+ }
133
147
  /** Read the current session — safe in Server Components (no cookie write). */
134
148
  async function auth(overrides) {
135
149
  const config = resolveNextConfig(overrides);
@@ -187,6 +201,7 @@ function createAuthHandlers(handlersConfig = {}) {
187
201
  return Response.json({ status: "invalid_request" }, { status: 400 });
188
202
  }
189
203
  const geoHeaders = geoHeadersFromBody(payload.geo);
204
+ const deviceContextHeaders = deviceContextHeadersFromBody(payload.deviceContext);
190
205
  const email = typeof payload.email === "string" ? payload.email : "";
191
206
  const password = typeof payload.password === "string" ? payload.password : "";
192
207
  if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
@@ -210,7 +225,8 @@ function createAuthHandlers(handlersConfig = {}) {
210
225
  case "authorized":
211
226
  await newSession(adapt(await cookies()), config, secure).establishFromCode(outcome.code, verifier, redirectUri, {
212
227
  ...collectForwardedDeviceHeaders(request),
213
- ...geoHeaders
228
+ ...geoHeaders,
229
+ ...deviceContextHeaders
214
230
  });
215
231
  return Response.json({
216
232
  status: "success",
@@ -340,6 +356,19 @@ function createAuthHandlers(handlersConfig = {}) {
340
356
  },
341
357
  async session() {
342
358
  return Response.json(await auth(handlersConfig));
359
+ },
360
+ async subscriptionCredential() {
361
+ const config = resolveNextConfig(handlersConfig);
362
+ const current = await new ServerSession({
363
+ authClient: buildServerAuthClient(config),
364
+ secret: config.secret,
365
+ cookies: await requestCookies()
366
+ }).read();
367
+ if (!current) return Response.json({ status: "signed_out" }, { status: 401 });
368
+ return Response.json({
369
+ token: current.tokens.accessToken,
370
+ organizationId: current.session.claims.organizationId
371
+ });
343
372
  }
344
373
  };
345
374
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poly-x/next",
3
- "version": "0.1.0-alpha.17",
3
+ "version": "0.1.0-alpha.19",
4
4
  "description": "PolyX SDK for Next.js App Router - components plus server helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,8 +47,8 @@
47
47
  "module": "./dist/index.mjs",
48
48
  "types": "./dist/index.d.cts",
49
49
  "dependencies": {
50
- "@poly-x/core": "0.1.0-alpha.17",
51
- "@poly-x/react": "0.1.0-alpha.17"
50
+ "@poly-x/core": "0.1.0-alpha.19",
51
+ "@poly-x/react": "0.1.0-alpha.19"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "next": ">=14",