@c1m-public/platform-auth 0.8.2 → 0.8.4

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.
@@ -308,6 +308,16 @@ function createHandoffSource(config) {
308
308
  // src/session-refresh.ts
309
309
  var DEFAULT_TTL_MS = 5e3;
310
310
  var DEFAULT_MAX = 1e3;
311
+ function parseRefreshEnvelope(value) {
312
+ if (!value || typeof value !== "object") return null;
313
+ const outer = value;
314
+ const payload = outer.data && typeof outer.data === "object" ? outer.data : value;
315
+ if (typeof payload.access_token !== "string" || !payload.access_token) return null;
316
+ return {
317
+ accessToken: payload.access_token,
318
+ refreshToken: typeof payload.refresh_token === "string" && payload.refresh_token ? payload.refresh_token : void 0
319
+ };
320
+ }
311
321
  function createSessionRefresher(config) {
312
322
  const ttl = config.singleflightTtlMs ?? DEFAULT_TTL_MS;
313
323
  const max = config.maxSingleflights ?? DEFAULT_MAX;
@@ -330,8 +340,8 @@ function createSessionRefresher(config) {
330
340
  fetch: config.fetch
331
341
  });
332
342
  if (!response.ok) return null;
333
- const data = parseAuthResponse(await response.json());
334
- return data && hasTokenAudience(data.access_token, config.audience) ? { accessToken: data.access_token, refreshToken: data.refresh_token } : null;
343
+ const refreshed = parseRefreshEnvelope(await response.json());
344
+ return refreshed && hasTokenAudience(refreshed.accessToken, config.audience) ? refreshed : null;
335
345
  } catch {
336
346
  return null;
337
347
  }
@@ -391,4 +401,4 @@ async function evaluateSessionGate(input) {
391
401
  return { kind: "redirect_login" };
392
402
  }
393
403
 
394
- export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath };
404
+ export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath };
package/dist/index.d.ts CHANGED
@@ -189,6 +189,13 @@ interface SessionRefresherConfig {
189
189
  fetchTimeoutMs?: number;
190
190
  fetch?: typeof fetch;
191
191
  }
192
+ /**
193
+ * Two refresh envelopes exist: the platform one (flat, with token_type and
194
+ * expires_at) and the end-user one (`{ data: { access_token, refresh_token? } }`,
195
+ * where a rotated refresh token is optional). Both carry the access token the
196
+ * same way; that is all a refresh needs.
197
+ */
198
+ declare function parseRefreshEnvelope(value: unknown): RefreshedSession | null;
192
199
  declare function createSessionRefresher(config: SessionRefresherConfig): {
193
200
  refresh(refreshToken: string): Promise<RefreshedSession | null>;
194
201
  size(): number;
@@ -221,4 +228,4 @@ interface SessionGateInput {
221
228
  }
222
229
  declare function evaluateSessionGate(input: SessionGateInput): Promise<SessionGateDecision>;
223
230
 
224
- export { AUTH_ERROR_MESSAGES, type AuthResponse, type CookieOptions, type DeviceIdentity, type DeviceIdentityConfig, type DeviceIdentityStore, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, type HandoffSource, type HandoffSourceConfig, type HandoffSourceTarget, type HandoffTarget, type HandoffTargetConfig, type RefreshedSession, SSO_ERROR_MESSAGES, type SessionData, type SessionGateDecision, type SessionGateInput, type SessionRefresher, type SessionRefresherConfig, type SessionUser, type TokenPayload, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
231
+ export { AUTH_ERROR_MESSAGES, type AuthResponse, type CookieOptions, type DeviceIdentity, type DeviceIdentityConfig, type DeviceIdentityStore, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, type HandoffSource, type HandoffSourceConfig, type HandoffSourceTarget, type HandoffTarget, type HandoffTargetConfig, type RefreshedSession, SSO_ERROR_MESSAGES, type SessionData, type SessionGateDecision, type SessionGateInput, type SessionRefresher, type SessionRefresherConfig, type SessionUser, type TokenPayload, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath } from './chunk-GQ36R3RQ.js';
1
+ export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath } from './chunk-Q7YI7RMG.js';
2
2
  export { decodeBase64Url, decodeDefaultSession, decodeTokenPayload, hasTokenAudience, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, tokenMaxAgeSeconds } from './chunk-FBC2NUH7.js';
3
3
  export { applyForwardedClientHeaders, forwardedRequestHeaders } from './chunk-ZWSJVE5S.js';
package/dist/next.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SessionData, CookieOptions } from './index.js';
2
- export { AUTH_ERROR_MESSAGES, AuthResponse, DeviceIdentity, DeviceIdentityConfig, DeviceIdentityStore, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, HandoffSource, HandoffSourceConfig, HandoffSourceTarget, HandoffTarget, HandoffTargetConfig, RefreshedSession, SSO_ERROR_MESSAGES, SessionGateDecision, SessionGateInput, SessionRefresher, SessionRefresherConfig, SessionUser, TokenPayload, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds } from './index.js';
2
+ export { AUTH_ERROR_MESSAGES, AuthResponse, DeviceIdentity, DeviceIdentityConfig, DeviceIdentityStore, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, HandoffSource, HandoffSourceConfig, HandoffSourceTarget, HandoffTarget, HandoffTargetConfig, RefreshedSession, SSO_ERROR_MESSAGES, SessionGateDecision, SessionGateInput, SessionRefresher, SessionRefresherConfig, SessionUser, TokenPayload, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, decodeBase64Url, decodeDefaultSession, decodeTokenPayload, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, hasTokenAudience, isAuthErrorCode, isHandoffErrorCode, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath, tokenMaxAgeSeconds } from './index.js';
3
3
  export { applyForwardedClientHeaders, forwardedRequestHeaders } from '@c1m-public/transport';
4
4
  import { R as Resolvable, A as AsyncResolvable } from './internal-D6RafCm2.js';
5
5
 
@@ -128,6 +128,11 @@ interface SessionStoreConfig {
128
128
  /** Called when a cookie read/write fails outside a request (prerender); default swallows. */
129
129
  onError?: (error: unknown, operation: string) => void;
130
130
  }
131
+ /** Per-call overrides: a multi-host surface picks the cookie domain per request. */
132
+ interface SessionWriteOptions {
133
+ /** Ignored for `__Host-` names, which forbid Domain. */
134
+ cookieDomain?: string;
135
+ }
131
136
  /**
132
137
  * Session cookies for a first-party Next.js platform surface, written once.
133
138
  *
@@ -148,24 +153,24 @@ interface SessionStoreConfig {
148
153
  declare function createSessionStore(config: SessionStoreConfig): {
149
154
  audience: string;
150
155
  cookieNames: SessionCookieNames;
151
- cookieOptions: (rememberMe: boolean, token?: string) => CookieOptions & {
156
+ cookieOptions: (rememberMe: boolean, token?: string, options?: SessionWriteOptions) => CookieOptions & {
152
157
  httpOnly: true;
153
158
  sameSite: "lax";
154
159
  path: "/";
155
160
  };
156
- createSession: (accessToken: string, refreshToken?: string, rememberMe?: boolean) => Promise<void>;
161
+ createSession: (accessToken: string, refreshToken?: string, rememberMe?: boolean, options?: SessionWriteOptions) => Promise<void>;
157
162
  writeSessionCookies: (response: Response, accessToken: string, refreshToken?: string, rememberMe?: boolean) => Response;
158
163
  getAccessToken: () => Promise<string | null>;
159
164
  getRefreshToken: () => Promise<string | null>;
160
165
  getSession: () => Promise<SessionData | null>;
161
166
  getSessionWithRefresh: () => Promise<SessionData | null>;
162
167
  isRememberMe: () => Promise<boolean>;
163
- updateAccessToken: (accessToken: string, refreshToken?: string) => Promise<void>;
164
- destroySession: () => Promise<void>;
168
+ updateAccessToken: (accessToken: string, refreshToken?: string, options?: SessionWriteOptions) => Promise<void>;
169
+ destroySession: (options?: SessionWriteOptions) => Promise<void>;
165
170
  clearSessionCookies: (response: Response) => Response;
166
171
  requireSession: () => Promise<SessionData>;
167
172
  isAuthenticated(): Promise<boolean>;
168
173
  };
169
174
  type SessionStore = ReturnType<typeof createSessionStore>;
170
175
 
171
- export { CookieOptions, type PlatformFetch, type PlatformFetchConfig, type PlatformFetchTimeouts, PlatformServerFetchError, RecentAuthenticationRequiredError, SSOReauthenticationRequiredError, type SessionCookieNames, SessionData, SessionExpiredError, type SessionStore, type SessionStoreConfig, type SessionStoreRefreshConfig, authenticationErrorFor, buildForwardedRequestHeaders, controlRequest, createPlatformFetch, createSessionStore, isPlatformServerFetchError, isRecentAuthenticationRequiredError, isSSOReauthenticationRequiredError, isSessionExpiredError, mapControlError };
176
+ export { CookieOptions, type PlatformFetch, type PlatformFetchConfig, type PlatformFetchTimeouts, PlatformServerFetchError, RecentAuthenticationRequiredError, SSOReauthenticationRequiredError, type SessionCookieNames, SessionData, SessionExpiredError, type SessionStore, type SessionStoreConfig, type SessionStoreRefreshConfig, type SessionWriteOptions, authenticationErrorFor, buildForwardedRequestHeaders, controlRequest, createPlatformFetch, createSessionStore, isPlatformServerFetchError, isRecentAuthenticationRequiredError, isSSOReauthenticationRequiredError, isSessionExpiredError, mapControlError };
package/dist/next.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createSessionRefresher, setCookie, deleteCookie } from './chunk-GQ36R3RQ.js';
2
- export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath } from './chunk-GQ36R3RQ.js';
1
+ import { createSessionRefresher, setCookie, deleteCookie } from './chunk-Q7YI7RMG.js';
2
+ export { AUTH_ERROR_MESSAGES, HANDOFF_ERROR_FALLBACK, HANDOFF_ERROR_MESSAGES, SSO_ERROR_MESSAGES, authErrorMessage, brokerPaths, createDeviceIdentity, createHandoffSource, createHandoffTarget, createSessionRefresher, deleteCookie, evaluateSessionGate, handoffErrorMessage, hasSessionShape, isAuthErrorCode, isHandoffErrorCode, parseRefreshEnvelope, readCookie, safeInternalPath, serializeCookie, setCookie, sourceStartPath } from './chunk-Q7YI7RMG.js';
3
3
  import { decodeDefaultSession, tokenMaxAgeSeconds, hasTokenAudience, isTokenExpiringSoon, isTokenExpired } from './chunk-FBC2NUH7.js';
4
4
  export { decodeBase64Url, decodeDefaultSession, decodeTokenPayload, hasTokenAudience, isTokenExpired, isTokenExpiringSoon, parseAuthResponse, tokenMaxAgeSeconds } from './chunk-FBC2NUH7.js';
5
5
  import { applyForwardedClientHeaders, resolveValue } from './chunk-ZWSJVE5S.js';
@@ -176,15 +176,16 @@ function createSessionStore(config) {
176
176
  timeoutMs: config.refresh.timeoutMs,
177
177
  fetch: config.refresh.fetch
178
178
  }) : void 0;
179
- function cookieOptions(rememberMe, token) {
179
+ function cookieOptions(rememberMe, token, options) {
180
180
  const maxAge = token ? tokenMaxAgeSeconds(token, maxAgeCap) : maxAgeCap;
181
+ const effectiveDomain = hostPrefixed ? void 0 : options?.cookieDomain ?? domain;
181
182
  return {
182
183
  httpOnly: true,
183
184
  secure,
184
185
  sameSite: "lax",
185
186
  path: "/",
186
187
  ...rememberMe ? { maxAge } : {},
187
- ...domain ? { domain } : {}
188
+ ...effectiveDomain ? { domain: effectiveDomain } : {}
188
189
  };
189
190
  }
190
191
  function assertAudience(token) {
@@ -207,13 +208,13 @@ function createSessionStore(config) {
207
208
  function rememberedIn(store) {
208
209
  return store?.get(names.rememberMe)?.value === "true";
209
210
  }
210
- async function createSession(accessToken, refreshToken, rememberMe = config.defaultRememberMe ?? true) {
211
+ async function createSession(accessToken, refreshToken, rememberMe = config.defaultRememberMe ?? true, options) {
211
212
  assertAudience(accessToken);
212
213
  const store = await jar("createSession");
213
214
  if (!store) throw new Error("Cannot create a session outside a request");
214
- store.set(names.session, accessToken, cookieOptions(rememberMe, accessToken));
215
- if (refreshToken) store.set(names.refreshToken, refreshToken, cookieOptions(rememberMe, refreshToken));
216
- store.set(names.rememberMe, rememberMe ? "true" : "false", cookieOptions(rememberMe));
215
+ store.set(names.session, accessToken, cookieOptions(rememberMe, accessToken, options));
216
+ if (refreshToken) store.set(names.refreshToken, refreshToken, cookieOptions(rememberMe, refreshToken, options));
217
+ store.set(names.rememberMe, rememberMe ? "true" : "false", cookieOptions(rememberMe, void 0, options));
217
218
  }
218
219
  function writeSessionCookies(response, accessToken, refreshToken, rememberMe = config.defaultRememberMe ?? true) {
219
220
  assertAudience(accessToken);
@@ -238,13 +239,13 @@ function createSessionStore(config) {
238
239
  async function isRememberMe() {
239
240
  return rememberedIn(await jar("isRememberMe"));
240
241
  }
241
- async function updateAccessToken(accessToken, refreshToken) {
242
+ async function updateAccessToken(accessToken, refreshToken, options) {
242
243
  assertAudience(accessToken);
243
244
  const store = await jar("updateAccessToken");
244
245
  if (!store) throw new Error("Cannot update a session outside a request");
245
246
  const rememberMe = rememberedIn(store);
246
- store.set(names.session, accessToken, cookieOptions(rememberMe, accessToken));
247
- if (refreshToken) store.set(names.refreshToken, refreshToken, cookieOptions(rememberMe, refreshToken));
247
+ store.set(names.session, accessToken, cookieOptions(rememberMe, accessToken, options));
248
+ if (refreshToken) store.set(names.refreshToken, refreshToken, cookieOptions(rememberMe, refreshToken, options));
248
249
  }
249
250
  async function getSessionWithRefresh() {
250
251
  const store = await jar("getSessionWithRefresh");
@@ -264,12 +265,12 @@ function createSessionStore(config) {
264
265
  function clearOptions() {
265
266
  return { path: "/", secure };
266
267
  }
267
- async function destroySession() {
268
+ async function destroySession(options) {
268
269
  const store = await jar("destroySession");
269
270
  if (!store) return;
270
271
  try {
271
- const options = cookieOptions(false);
272
- for (const name of [names.session, names.refreshToken, names.rememberMe]) store.delete({ name, ...options });
272
+ const attributes = cookieOptions(false, void 0, options);
273
+ for (const name of [names.session, names.refreshToken, names.rememberMe]) store.delete({ name, ...attributes });
273
274
  } catch (error) {
274
275
  onError(error, "destroySession");
275
276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c1m-public/platform-auth",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Session and authorization helpers for C1M platform surfaces. The root is runtime-neutral (Web Request/Response): token/session reads, the first-party handoff (PKCE target + source), proxy session gate and coalesced refresh; ./next adds the server-only guard; ./access carries the resolved access snapshot, typed permission keys, predicates, a Next loader and React bindings",