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.
- package/README.md +261 -50
- package/dist/{NajmAuthClient-Cn9bObLB.d.ts → NajmAuthClient-DqGucYXi.d.ts} +3 -141
- package/dist/client/edge.d.ts +3 -18
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +15 -8
- package/dist/client/react/index.d.ts +11 -6
- package/dist/client/react/index.js +10 -3
- package/dist/client/server/index.d.ts +25 -78
- package/dist/client/server/index.js +104 -60
- package/dist/client/server/react.d.ts +48 -0
- package/dist/client/server/react.js +71 -0
- package/dist/client/server/reactClientGuard.d.ts +2 -0
- package/dist/client/server/reactClientGuard.js +4 -0
- package/dist/getSession-BthP85UA.d.ts +68 -0
- package/dist/identity/ma.d.ts +1 -0
- package/dist/identity/ma.js +64 -0
- package/dist/index.d.ts +395 -91
- package/dist/index.js +1733 -862
- package/dist/ma-sNHnUGLO.d.ts +88 -0
- package/dist/schema/pg.d.ts +260 -1
- package/dist/schema/pg.js +13 -0
- package/dist/schema/sqlite.d.ts +284 -1
- package/dist/schema/sqlite.js +14 -1
- package/dist/sessionRecovery-D5Fa0yZ1.d.ts +18 -0
- package/dist/types-BaSfgxqE.d.ts +166 -0
- package/package.json +15 -2
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
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
|
|
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
|
|
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
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
return null;
|
|
423
|
+
return unauthenticated("No recoverable refresh session");
|
|
427
424
|
}
|
|
428
425
|
if (!requestOrigin) {
|
|
429
|
-
|
|
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 (
|
|
452
|
-
|
|
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
|
-
|
|
944
|
-
|
|
945
|
-
)
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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" &&
|
|
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))
|
|
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,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
|
+
);
|