najm-auth 2.0.15 → 3.1.0

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.
@@ -1,6 +1,10 @@
1
- import { f as AuthUser, F as FetchClient, N as NajmAuthClient, h as RetryConfig } from '../../NajmAuthClient-Cn9bObLB.js';
2
- import { SessionRecoveryFailure } from '../edge.js';
3
- export { SessionRecoveryErrorDetails, SessionRecoveryFailureReason, withAuthMiddleware } from '../edge.js';
1
+ import { A as AuthUser, R as RetryConfig } from '../../types-BaSfgxqE.js';
2
+ import { F as FetchClient, N as NajmAuthClient } from '../../NajmAuthClient-DqGucYXi.js';
3
+ export { withAuthMiddleware } from '../edge.js';
4
+ import { G as GetSessionConfig, S as ServerSession$1 } from '../../getSession-BthP85UA.js';
5
+ export { A as AuthConfigError, a as AuthTransportError, N as NoSessionError, g as getSession } from '../../getSession-BthP85UA.js';
6
+ import { S as SessionRecoveryFailure } from '../../sessionRecovery-D5Fa0yZ1.js';
7
+ export { a as SessionRecoveryErrorDetails, b as SessionRecoveryFailureReason } from '../../sessionRecovery-D5Fa0yZ1.js';
4
8
  import 'next/server';
5
9
 
6
10
  interface GetServerSessionOptions {
@@ -11,7 +15,7 @@ interface GetServerSessionOptions {
11
15
  /** Additional headers to forward (e.g., Authorization) */
12
16
  headers?: Record<string, string>;
13
17
  }
14
- interface ServerSession$1 {
18
+ interface ServerSession {
15
19
  user: AuthUser;
16
20
  }
17
21
  /**
@@ -35,7 +39,7 @@ interface ServerSession$1 {
35
39
  * }
36
40
  * ```
37
41
  */
38
- declare function getServerSession(opts: GetServerSessionOptions): Promise<ServerSession$1 | null>;
42
+ declare function getServerSession(opts: GetServerSessionOptions): Promise<ServerSession | null>;
39
43
 
40
44
  interface ServerClientConfig {
41
45
  /** API base URL (e.g., 'http://localhost:3000/api') */
@@ -65,70 +69,6 @@ interface ServerClientConfig {
65
69
  */
66
70
  declare function createServerClient(config: ServerClientConfig): FetchClient;
67
71
 
68
- interface ServerSession {
69
- user: AuthUser;
70
- roles?: string[];
71
- permissions?: string[];
72
- }
73
- interface GetSessionConfig {
74
- /**
75
- * Base URL for auth endpoints.
76
- * Defaults to `NEXT_PUBLIC_API_URL` or the same-origin `/api` path.
77
- */
78
- baseURL?: string;
79
- /** Auth route prefix appended to baseURL (default: '/auth'). */
80
- authPrefix?: string;
81
- /** Refresh token cookie name (default: 'refreshToken'). */
82
- cookieName?: string;
83
- /** Signed session cookie name (default: 'najm.session'). */
84
- sessionCookieName?: string;
85
- /**
86
- * Secret used to verify the session cookie HMAC signature.
87
- * Falls back to NAJM_SESSION_SECRET or JWT_ACCESS_SECRET env vars.
88
- */
89
- sessionSecret?: string;
90
- /**
91
- * Maximum accepted session-cookie age in seconds.
92
- * Must match the auth plugin's `session.maxAge`. Default: 300.
93
- */
94
- sessionMaxAge?: number;
95
- /**
96
- * Session-recovery endpoint. Defaults to
97
- * `${baseURL}${authPrefix}/session/recover`. Set to false to disable fallback.
98
- */
99
- recoveryURL?: string | false;
100
- /** Loopback-only recovery endpoint for self-hosted reverse-proxy setups. */
101
- internalRecoveryURL?: string;
102
- /**
103
- * Error handling mode:
104
- * - 'nullable' (default): returns null on any failure
105
- * - 'strict': throws typed errors for debugging
106
- */
107
- mode?: 'nullable' | 'strict';
108
- /** Secret-free diagnostic hook for failed recovery attempts. */
109
- onRecoveryFailure?: (failure: SessionRecoveryFailure) => void;
110
- }
111
- declare class NoSessionError extends Error {
112
- readonly code = "NO_SESSION";
113
- constructor(message?: string);
114
- }
115
- declare class AuthConfigError extends Error {
116
- readonly code = "AUTH_CONFIG_ERROR";
117
- constructor(message: string);
118
- }
119
- declare class AuthTransportError extends Error {
120
- readonly status?: number;
121
- readonly code = "AUTH_TRANSPORT_ERROR";
122
- constructor(message: string, status?: number);
123
- }
124
- /**
125
- * Resolve a session in a Next.js Server Component, Route Handler, or Server
126
- * Action. Recovery returns claims for the current render but cannot persist
127
- * response cookies during Server Component rendering; middleware performs that
128
- * persistence for protected navigation.
129
- */
130
- declare function getSession(config?: GetSessionConfig): Promise<ServerSession | null>;
131
-
132
72
  interface WithAuthOptions extends GetSessionConfig {
133
73
  /** Where to redirect unauthenticated requests (default: '/login') */
134
74
  redirectTo?: string;
@@ -138,7 +78,7 @@ interface WithAuthOptions extends GetSessionConfig {
138
78
  permission?: string;
139
79
  }
140
80
  interface WithAuthProps<P> {
141
- session: ServerSession;
81
+ session: ServerSession$1;
142
82
  props: P;
143
83
  }
144
84
  /**
@@ -219,9 +159,9 @@ interface AuthKit {
219
159
  /** Shortcut for `client.api` — the underlying FetchClient with auth attached. */
220
160
  readonly api: FetchClient;
221
161
  /** Resolve session — signed-cookie first, then non-rotating recovery. */
222
- getSession: (opts?: Pick<GetSessionConfig, 'mode'>) => Promise<ServerSession | null>;
162
+ getSession: (opts?: Pick<GetSessionConfig, 'mode'>) => Promise<ServerSession$1 | null>;
223
163
  /** Require session — throws if unauthenticated */
224
- requireSession: () => Promise<ServerSession>;
164
+ requireSession: () => Promise<ServerSession$1>;
225
165
  /**
226
166
  * Require one of `roles` — redirects to `loginRoute` when unauthenticated and
227
167
  * to `forbiddenRoute` when authenticated as the wrong role.
@@ -230,7 +170,7 @@ interface AuthKit {
230
170
  * const session = await auth.requireRole(['admin', 'operator']);
231
171
  * ```
232
172
  */
233
- requireRole: (roles: string[]) => Promise<ServerSession>;
173
+ requireRole: (roles: string[]) => Promise<ServerSession$1>;
234
174
  /** Generated Next.js middleware function */
235
175
  middleware: (request: Request) => Promise<Response>;
236
176
  /** Next.js middleware config with matcher */
@@ -242,7 +182,7 @@ interface AuthKit {
242
182
  * Passes session to the wrapped component.
243
183
  */
244
184
  protect: <P extends Record<string, unknown> = Record<string, unknown>>(Page: (args: {
245
- session: ServerSession;
185
+ session: ServerSession$1;
246
186
  children?: unknown;
247
187
  } & P) => Promise<unknown> | unknown, options?: {
248
188
  role?: string;
@@ -296,13 +236,20 @@ interface AuthCookiePersistenceOptions {
296
236
  logoutPaths?: string[];
297
237
  /** Paths that reissue cookies and must reapply the stored choice. */
298
238
  refreshPaths?: string[];
239
+ /**
240
+ * Paths that finish credential setup. The stored choice is cleared there:
241
+ * the login it was recorded for never produced a session.
242
+ */
243
+ setupCompletionPaths?: string[];
299
244
  /**
300
245
  * Recognizes a response that has *not* issued a usable session because the
301
246
  * user must still set up credentials.
302
247
  *
303
- * Such a response may carry auth cookies anyway, and persisting them would
304
- * leave a half-authenticated browser that skips the setup step on reload.
305
- * Returning `true` strips them and clears the stored choice.
248
+ * Najm's own setup response is recognized without this supply it only to
249
+ * cover an application-specific shape. Such a response may carry auth
250
+ * cookies anyway, and persisting them would leave a half-authenticated
251
+ * browser that skips the setup step on reload. Returning `true` strips them
252
+ * and clears the stored choice.
306
253
  */
307
254
  isSetupResponse?: (payload: unknown) => boolean;
308
255
  }
@@ -325,4 +272,4 @@ declare function makeSessionCookie(setCookie: string, authCookieNames?: string[]
325
272
  */
326
273
  declare function withAuthCookiePersistence(handler: RequestHandler, options?: AuthCookiePersistenceOptions): RequestHandler;
327
274
 
328
- export { AuthConfigError, type AuthCookiePersistenceOptions, type AuthKit, AuthTransportError, type DefineAuthConfig, type GetSessionConfig, NoSessionError, type SafeRedirectOptions, type ServerSession, SessionRecoveryFailure, type WithAuthOptions, type WithAuthProps, createServerClient, defineAuth, getSafeRedirectPath, getServerSession, getSession, makeSessionCookie, withAuth, withAuthCookiePersistence };
275
+ export { type AuthCookiePersistenceOptions, type AuthKit, type DefineAuthConfig, GetSessionConfig, type SafeRedirectOptions, ServerSession$1 as ServerSession, SessionRecoveryFailure, type WithAuthOptions, type WithAuthProps, createServerClient, defineAuth, getSafeRedirectPath, getServerSession, makeSessionCookie, withAuth, withAuthCookiePersistence };
@@ -356,7 +356,8 @@ __export(getSession_exports, {
356
356
  AuthConfigError: () => AuthConfigError,
357
357
  AuthTransportError: () => AuthTransportError,
358
358
  NoSessionError: () => NoSessionError,
359
- getSession: () => getSession
359
+ getSession: () => getSession,
360
+ resolveSessionOutcome: () => resolveSessionOutcome
360
361
  });
361
362
  function defaultBaseURL() {
362
363
  const explicit = typeof process !== "undefined" ? process.env.NAJM_AUTH_BASE_URL : void 0;
@@ -381,12 +382,11 @@ function requestOriginFromHeaders(headers) {
381
382
  return void 0;
382
383
  }
383
384
  }
384
- async function getSession(config = {}) {
385
+ async function resolveSessionOutcome(config = {}) {
385
386
  const cookieName2 = config.cookieName ?? "refreshToken";
386
387
  const sessionCookieName = config.sessionCookieName ?? "najm.session";
387
388
  const baseURL = config.baseURL ?? defaultBaseURL();
388
389
  const prefix = config.authPrefix ?? "/auth";
389
- const strict = config.mode === "strict";
390
390
  const internalRecoveryURL = resolveInternalRecoveryURL(config.internalRecoveryURL);
391
391
  let sessionCookieValue;
392
392
  let refreshCookieValue;
@@ -400,8 +400,7 @@ async function getSession(config = {}) {
400
400
  requestOrigin = requestOriginFromHeaders(await mod.headers());
401
401
  }
402
402
  } catch {
403
- if (strict) throw new AuthConfigError("Failed to read cookies from Next.js headers()");
404
- return null;
403
+ return failed(new AuthConfigError("Failed to read cookies from Next.js headers()"));
405
404
  }
406
405
  const secret = resolveSessionSecret(config.sessionSecret);
407
406
  if (sessionCookieValue && secret) {
@@ -410,24 +409,21 @@ async function getSession(config = {}) {
410
409
  maxAgeSeconds: config.sessionMaxAge
411
410
  });
412
411
  if (claims) {
413
- return {
412
+ return authenticated({
414
413
  user: claims.user,
415
414
  roles: claims.roles,
416
415
  permissions: claims.permissions
417
- };
416
+ });
418
417
  }
419
418
  }
420
419
  if (!secret) {
421
- if (strict) throw new AuthConfigError("Session cookie secret is not configured");
422
- return null;
420
+ return failed(new AuthConfigError("Session cookie secret is not configured"));
423
421
  }
424
422
  if (!refreshCookieValue || config.recoveryURL === false && !internalRecoveryURL) {
425
- if (strict) throw new NoSessionError("No recoverable refresh session");
426
- return null;
423
+ return unauthenticated("No recoverable refresh session");
427
424
  }
428
425
  if (!requestOrigin) {
429
- if (strict) throw new AuthConfigError("Incoming request origin is unavailable");
430
- return null;
426
+ return failed(new AuthConfigError("Incoming request origin is unavailable"));
431
427
  }
432
428
  const endpoint = internalRecoveryURL ?? (config.recoveryURL ? new URL(config.recoveryURL, requestOrigin).toString() : authEndpoint(baseURL, prefix, "/session/recover", requestOrigin));
433
429
  const recovery = await requestSessionRecovery({
@@ -442,24 +438,27 @@ async function getSession(config = {}) {
442
438
  onFailure: config.onRecoveryFailure
443
439
  });
444
440
  if (recovery.status === "recovered") {
445
- return {
441
+ return authenticated({
446
442
  user: recovery.claims.user,
447
443
  roles: recovery.claims.roles,
448
444
  permissions: recovery.claims.permissions
449
- };
445
+ });
450
446
  }
451
- if (strict) {
452
- if (recovery.status === "invalid") {
453
- throw new NoSessionError("Refresh session is invalid or revoked");
454
- }
455
- throw new AuthTransportError(
456
- "Session recovery endpoint was unavailable or returned an invalid session",
457
- recovery.httpStatus
458
- );
447
+ if (recovery.status === "invalid") {
448
+ return unauthenticated("Refresh session is invalid or revoked");
459
449
  }
450
+ return failed(new AuthTransportError(
451
+ "Session recovery endpoint was unavailable or returned an invalid session",
452
+ recovery.httpStatus
453
+ ));
454
+ }
455
+ async function getSession(config = {}) {
456
+ const outcome = await resolveSessionOutcome(config);
457
+ if (outcome.status === "authenticated") return outcome.session;
458
+ if (config.mode === "strict") throw outcome.error;
460
459
  return null;
461
460
  }
462
- var NoSessionError, AuthConfigError, AuthTransportError;
461
+ var NoSessionError, AuthConfigError, AuthTransportError, authenticated, unauthenticated, failed;
463
462
  var init_getSession = __esm({
464
463
  "src/client/server/getSession.ts"() {
465
464
  init_sessionCookie();
@@ -498,6 +497,19 @@ var init_getSession = __esm({
498
497
  __name(defaultBaseURL, "defaultBaseURL");
499
498
  __name(firstForwardedValue, "firstForwardedValue");
500
499
  __name(requestOriginFromHeaders, "requestOriginFromHeaders");
500
+ authenticated = /* @__PURE__ */ __name((session) => ({
501
+ status: "authenticated",
502
+ session
503
+ }), "authenticated");
504
+ unauthenticated = /* @__PURE__ */ __name((message) => ({
505
+ status: "unauthenticated",
506
+ error: new NoSessionError(message)
507
+ }), "unauthenticated");
508
+ failed = /* @__PURE__ */ __name((error) => ({
509
+ status: "failed",
510
+ error
511
+ }), "failed");
512
+ __name(resolveSessionOutcome, "resolveSessionOutcome");
501
513
  __name(getSession, "getSession");
502
514
  }
503
515
  });
@@ -888,6 +900,10 @@ var TabSync = class {
888
900
  };
889
901
 
890
902
  // src/client/NajmAuthClient.ts
903
+ function isCredentialSetupPending(payload) {
904
+ return typeof payload === "object" && payload !== null && payload.nextStep === "credential_setup";
905
+ }
906
+ __name(isCredentialSetupPending, "isCredentialSetupPending");
891
907
  var INITIAL_STATE = {
892
908
  user: null,
893
909
  accessToken: null,
@@ -939,20 +955,23 @@ var NajmAuthClient = class _NajmAuthClient {
939
955
  // Auth Operations
940
956
  // =========================================================================
941
957
  async login(credentials) {
942
- const res = await this.api.post(
943
- `${this.prefix}/login`,
944
- { body: credentials, skipAuth: true }
945
- );
946
- this.applyTokens(res.data);
947
- if (res.data.user) {
948
- this.state = { ...this.state, user: res.data.user };
958
+ const res = await this.api.post(`${this.prefix}/login`, { body: credentials, skipAuth: true });
959
+ this.resetRefreshFailures();
960
+ const setup = isCredentialSetupPending(res) ? res : isCredentialSetupPending(res.data) ? res.data : null;
961
+ if (setup) {
962
+ return { ...setup };
963
+ }
964
+ const authenticated2 = res.data;
965
+ this.applyTokens(authenticated2);
966
+ if (authenticated2.user) {
967
+ this.state = { ...this.state, user: authenticated2.user };
949
968
  this.notify();
950
969
  } else {
951
970
  await this.fetchUser();
952
971
  }
953
972
  this.tabSync?.broadcastSync(this.getSyncPayload());
954
973
  this.emit("login", this.state.user);
955
- return this.state.user;
974
+ return { nextStep: "authenticated", user: this.state.user };
956
975
  }
957
976
  async register(data) {
958
977
  const res = await this.api.post(
@@ -1318,6 +1337,29 @@ function createAuthClient(config) {
1318
1337
  }
1319
1338
  __name(createAuthClient, "createAuthClient");
1320
1339
 
1340
+ // src/client/server/internals.ts
1341
+ var REACT_SERVER_INTERNALS = Symbol.for("najm-auth.reactServerInternals");
1342
+ function redirectsToLogin(outcome) {
1343
+ if (outcome.status === "unauthenticated") return true;
1344
+ const { code, status } = outcome.error;
1345
+ return code === "AUTH_TRANSPORT_ERROR" && (status === 401 || status === 403);
1346
+ }
1347
+ __name(redirectsToLogin, "redirectsToLogin");
1348
+ function heldRoles(session) {
1349
+ return session.roles ?? (session.user.role ? [session.user.role] : []);
1350
+ }
1351
+ __name(heldRoles, "heldRoles");
1352
+ function attachReactServerInternals(kit, internals) {
1353
+ Object.defineProperty(kit, REACT_SERVER_INTERNALS, {
1354
+ value: internals,
1355
+ enumerable: false,
1356
+ configurable: false,
1357
+ writable: false
1358
+ });
1359
+ return kit;
1360
+ }
1361
+ __name(attachReactServerInternals, "attachReactServerInternals");
1362
+
1321
1363
  // src/client/server/defineAuth.ts
1322
1364
  function defineAuth(authConfig = {}) {
1323
1365
  const {
@@ -1372,32 +1414,22 @@ function defineAuth(authConfig = {}) {
1372
1414
  const { getSession: resolveSession } = await Promise.resolve().then(() => (init_getSession(), getSession_exports));
1373
1415
  return resolveSession({ ...sessionConfig, ...opts });
1374
1416
  }, "getSession");
1417
+ const resolveSessionOutcome2 = /* @__PURE__ */ __name(async () => {
1418
+ const { resolveSessionOutcome: resolve } = await Promise.resolve().then(() => (init_getSession(), getSession_exports));
1419
+ return resolve(sessionConfig);
1420
+ }, "resolveSessionOutcome");
1375
1421
  const requireSession = /* @__PURE__ */ __name(async () => {
1376
- const sessionModule = await Promise.resolve().then(() => (init_getSession(), getSession_exports));
1377
- const {
1378
- getSession: resolveSession,
1379
- NoSessionError: NoSessionError2,
1380
- AuthTransportError: AuthTransportError2
1381
- } = sessionModule;
1382
- try {
1383
- const session = await resolveSession({ ...sessionConfig, mode: "strict" });
1384
- return session;
1385
- } catch (err) {
1386
- if (err instanceof NoSessionError2) {
1387
- const { redirect } = await import("next/navigation");
1388
- redirect(loginRoute);
1389
- }
1390
- if (err instanceof AuthTransportError2 && (err.status === 401 || err.status === 403)) {
1391
- const { redirect } = await import("next/navigation");
1392
- redirect(loginRoute);
1393
- }
1394
- throw err;
1422
+ const outcome = await resolveSessionOutcome2();
1423
+ if (outcome.status === "authenticated") return outcome.session;
1424
+ if (redirectsToLogin(outcome)) {
1425
+ const { redirect } = await import("next/navigation");
1426
+ redirect(loginRoute);
1395
1427
  }
1428
+ throw outcome.error;
1396
1429
  }, "requireSession");
1397
1430
  const requireRole = /* @__PURE__ */ __name(async (roles) => {
1398
1431
  const session = await requireSession();
1399
- const held = session.roles ?? (session.user.role ? [session.user.role] : []);
1400
- if (!held.some((role) => roles.includes(role))) {
1432
+ if (!heldRoles(session).some((role) => roles.includes(role))) {
1401
1433
  const { redirect } = await import("next/navigation");
1402
1434
  redirect(forbiddenRoute);
1403
1435
  }
@@ -1427,8 +1459,7 @@ function defineAuth(authConfig = {}) {
1427
1459
  redirect(loginRoute);
1428
1460
  }
1429
1461
  if (options?.role) {
1430
- const userRoles = session.roles ?? (session.user.role ? [session.user.role] : []);
1431
- if (!userRoles.includes(options.role)) {
1462
+ if (!heldRoles(session).includes(options.role)) {
1432
1463
  const { redirect } = await import("next/navigation");
1433
1464
  redirect(forbiddenRoute);
1434
1465
  }
@@ -1444,7 +1475,7 @@ function defineAuth(authConfig = {}) {
1444
1475
  return Page({ session, ...props });
1445
1476
  }, "ProtectedPage");
1446
1477
  }, "protect");
1447
- return {
1478
+ return attachReactServerInternals({
1448
1479
  get client() {
1449
1480
  return getClient();
1450
1481
  },
@@ -1457,7 +1488,7 @@ function defineAuth(authConfig = {}) {
1457
1488
  middleware,
1458
1489
  config: { matcher },
1459
1490
  protect
1460
- };
1491
+ }, { resolveSessionOutcome: resolveSessionOutcome2, loginRoute, forbiddenRoute });
1461
1492
  }
1462
1493
  __name(defineAuth, "defineAuth");
1463
1494
 
@@ -1487,8 +1518,17 @@ var DEFAULTS = {
1487
1518
  maxAgeSeconds: 7 * 24 * 60 * 60,
1488
1519
  loginPaths: ["/api/auth/login"],
1489
1520
  logoutPaths: ["/api/auth/logout"],
1490
- refreshPaths: ["/api/auth/refresh", "/api/auth/session/recover"]
1521
+ refreshPaths: ["/api/auth/refresh", "/api/auth/session/recover"],
1522
+ setupCompletionPaths: ["/api/auth/credential-setup/change"]
1491
1523
  };
1524
+ function isNajmSetupResponse(payload) {
1525
+ if (typeof payload !== "object" || payload === null) return false;
1526
+ const body = payload;
1527
+ if (body.nextStep === "credential_setup") return true;
1528
+ const data = body.data;
1529
+ return typeof data === "object" && data !== null && data.nextStep === "credential_setup";
1530
+ }
1531
+ __name(isNajmSetupResponse, "isNajmSetupResponse");
1492
1532
  function cookieValue(header, name) {
1493
1533
  for (const part of header.split(";")) {
1494
1534
  const separator = part.indexOf("=");
@@ -1547,6 +1587,7 @@ function withAuthCookiePersistence(handler, options = {}) {
1547
1587
  loginPaths = DEFAULTS.loginPaths,
1548
1588
  logoutPaths = DEFAULTS.logoutPaths,
1549
1589
  refreshPaths = DEFAULTS.refreshPaths,
1590
+ setupCompletionPaths = DEFAULTS.setupCompletionPaths,
1550
1591
  isSetupResponse
1551
1592
  } = options;
1552
1593
  const resolveAction = /* @__PURE__ */ __name(async (request) => {
@@ -1559,6 +1600,7 @@ function withAuthCookiePersistence(handler, options = {}) {
1559
1600
  };
1560
1601
  }
1561
1602
  if (logoutPaths.includes(pathname)) return { type: "clear" };
1603
+ if (setupCompletionPaths.includes(pathname)) return { type: "clear" };
1562
1604
  if (refreshPaths.includes(pathname)) {
1563
1605
  const remembered = cookieValue(
1564
1606
  request.headers.get("cookie") ?? "",
@@ -1596,9 +1638,11 @@ function withAuthCookiePersistence(handler, options = {}) {
1596
1638
  let action = await resolveAction(request);
1597
1639
  const response = await handler(request);
1598
1640
  if (!response.ok) return response;
1599
- if (action?.type === "apply" && isSetupResponse) {
1641
+ if (action?.type === "apply" && loginPaths.includes(new URL(request.url).pathname)) {
1600
1642
  const payload = await response.clone().json().catch(() => null);
1601
- if (isSetupResponse(payload)) action = { type: "setup" };
1643
+ if (isNajmSetupResponse(payload) || isSetupResponse?.(payload)) {
1644
+ action = { type: "setup" };
1645
+ }
1602
1646
  }
1603
1647
  if (!action) return response;
1604
1648
  return applyAction(
@@ -0,0 +1,48 @@
1
+ import { S as ServerSession } from '../../getSession-BthP85UA.js';
2
+ import '../../types-BaSfgxqE.js';
3
+ import '../../sessionRecovery-D5Fa0yZ1.js';
4
+
5
+ /**
6
+ * The `defineAuth()` result.
7
+ *
8
+ * Declared structurally rather than as `AuthKit` on purpose: each entry point
9
+ * bundles its own declarations, and `AuthKit` reaches classes whose private
10
+ * members would make the two copies nominally incompatible.
11
+ */
12
+ interface ReactServerAuthSource {
13
+ getSession(): Promise<ServerSession | null>;
14
+ requireSession(): Promise<ServerSession>;
15
+ requireRole(roles: string[]): Promise<ServerSession>;
16
+ }
17
+ interface ReactServerAuth {
18
+ /** The session for this request, or `null` when there is none. */
19
+ getSession(): Promise<ServerSession | null>;
20
+ /**
21
+ * The session for this request, redirecting to `loginRoute` when the visitor
22
+ * is unauthenticated.
23
+ *
24
+ * A recovery endpoint that is unreachable or a missing session secret is an
25
+ * operational fault, not an anonymous visitor: those stay visible errors
26
+ * rather than becoming a login redirect that hides the outage.
27
+ */
28
+ requireSession(): Promise<ServerSession>;
29
+ /**
30
+ * The session for this request, redirecting to `forbiddenRoute` when it holds
31
+ * none of `roles`. Not the login route — the visitor is already
32
+ * authenticated, so signing in again cannot change the answer.
33
+ */
34
+ requireRole(roles: string[]): Promise<ServerSession>;
35
+ }
36
+ /**
37
+ * Create the app's single request-scoped session accessor.
38
+ *
39
+ * Call this exactly once, at module scope, in a module the whole app imports.
40
+ * Calling it inside a layout, page, or component creates a fresh memoized
41
+ * resolver per call and shares nothing.
42
+ *
43
+ * Redirect routes and error classification come from the `defineAuth()` config
44
+ * that produced `auth`; they are not re-accepted here.
45
+ */
46
+ declare function createReactServerAuth(auth: ReactServerAuthSource): ReactServerAuth;
47
+
48
+ export { type ReactServerAuth, type ReactServerAuthSource, createReactServerAuth };
@@ -0,0 +1,71 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/client/server/react.ts
5
+ import * as React from "react";
6
+
7
+ // src/client/server/internals.ts
8
+ var REACT_SERVER_INTERNALS = Symbol.for("najm-auth.reactServerInternals");
9
+ function redirectsToLogin(outcome) {
10
+ if (outcome.status === "unauthenticated") return true;
11
+ const { code, status } = outcome.error;
12
+ return code === "AUTH_TRANSPORT_ERROR" && (status === 401 || status === 403);
13
+ }
14
+ __name(redirectsToLogin, "redirectsToLogin");
15
+ function heldRoles(session) {
16
+ return session.roles ?? (session.user.role ? [session.user.role] : []);
17
+ }
18
+ __name(heldRoles, "heldRoles");
19
+ function readReactServerInternals(auth) {
20
+ if (auth === null || typeof auth !== "object" && typeof auth !== "function") return void 0;
21
+ return auth[REACT_SERVER_INTERNALS];
22
+ }
23
+ __name(readReactServerInternals, "readReactServerInternals");
24
+
25
+ // src/client/server/react.ts
26
+ if (typeof window !== "undefined") {
27
+ throw new Error(
28
+ "najm-auth/client/server/react is a React Server Component module and cannot be imported from a Client Component. Import najm-auth/client/react instead."
29
+ );
30
+ }
31
+ function createReactServerAuth(auth) {
32
+ const internals = readReactServerInternals(auth);
33
+ if (!internals) {
34
+ throw new Error(
35
+ "createReactServerAuth() expects the object returned by defineAuth() from najm-auth/client/server. Check that both imports resolve to the same installed najm-auth version."
36
+ );
37
+ }
38
+ const cache2 = React.cache;
39
+ if (typeof cache2 !== "function") {
40
+ throw new Error(
41
+ "najm-auth/client/server/react requires a React version that exports cache() (React 18.3 or newer). Upgrade react, or use auth.getSession() directly."
42
+ );
43
+ }
44
+ const resolve = cache2(internals.resolveSessionOutcome);
45
+ const getSession = /* @__PURE__ */ __name(async () => {
46
+ const outcome = await resolve();
47
+ return outcome.status === "authenticated" ? outcome.session : null;
48
+ }, "getSession");
49
+ const requireSession = /* @__PURE__ */ __name(async () => {
50
+ const outcome = await resolve();
51
+ if (outcome.status === "authenticated") return outcome.session;
52
+ if (redirectsToLogin(outcome)) {
53
+ const { redirect } = await import("next/navigation");
54
+ redirect(internals.loginRoute);
55
+ }
56
+ throw outcome.error;
57
+ }, "requireSession");
58
+ const requireRole = /* @__PURE__ */ __name(async (roles) => {
59
+ const session = await requireSession();
60
+ if (!heldRoles(session).some((role) => roles.includes(role))) {
61
+ const { redirect } = await import("next/navigation");
62
+ redirect(internals.forbiddenRoute);
63
+ }
64
+ return session;
65
+ }, "requireRole");
66
+ return { getSession, requireSession, requireRole };
67
+ }
68
+ __name(createReactServerAuth, "createReactServerAuth");
69
+ export {
70
+ createReactServerAuth
71
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,4 @@
1
+ // src/client/server/reactClientGuard.ts
2
+ throw new Error(
3
+ "najm-auth/client/server/react is a React Server Component module. It cannot be imported from a Client Component, the Edge runtime, or any browser bundle. Use najm-auth/client/react for client components, and najm-auth/client/server for proxy and Edge code."
4
+ );