@imtbl/auth-nextjs 0.0.1-alpha.0 → 2.12.4-alpha.5

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.
@@ -0,0 +1,365 @@
1
+ 'use client';
2
+
3
+ // src/client/provider.tsx
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ useCallback,
11
+ useMemo
12
+ } from "react";
13
+ import {
14
+ SessionProvider,
15
+ useSession,
16
+ signIn,
17
+ signOut
18
+ } from "next-auth/react";
19
+ import {
20
+ Auth
21
+ } from "@imtbl/auth";
22
+
23
+ // src/utils/token.ts
24
+ import { decodeJwtPayload } from "@imtbl/auth";
25
+
26
+ // src/constants.ts
27
+ var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
28
+ var DEFAULT_AUDIENCE = "platform_api";
29
+ var DEFAULT_SCOPE = "openid profile email offline_access transact";
30
+ var IMMUTABLE_PROVIDER_ID = "immutable";
31
+ var DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
32
+ var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
33
+ var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
34
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
35
+
36
+ // src/utils/token.ts
37
+ function getTokenExpiry(accessToken) {
38
+ if (!accessToken) {
39
+ return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
40
+ }
41
+ try {
42
+ const payload = decodeJwtPayload(accessToken);
43
+ if (payload.exp && typeof payload.exp === "number") {
44
+ return payload.exp * 1e3;
45
+ }
46
+ return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
47
+ } catch {
48
+ return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
49
+ }
50
+ }
51
+
52
+ // src/client/provider.tsx
53
+ import { jsx } from "react/jsx-runtime";
54
+ var ImmutableAuthContext = createContext(null);
55
+ function ImmutableAuthInner({
56
+ children,
57
+ config,
58
+ basePath
59
+ }) {
60
+ const [auth, setAuth] = useState(null);
61
+ const prevConfigRef = useRef(null);
62
+ const [isAuthReady, setIsAuthReady] = useState(false);
63
+ const { data: session, update: updateSession } = useSession();
64
+ useEffect(() => {
65
+ if (typeof window === "undefined") return;
66
+ const configKey = [
67
+ config.clientId,
68
+ config.redirectUri,
69
+ config.logoutRedirectUri || "",
70
+ config.audience || DEFAULT_AUDIENCE,
71
+ config.scope || DEFAULT_SCOPE,
72
+ config.authenticationDomain || DEFAULT_AUTH_DOMAIN
73
+ ].join(":");
74
+ if (prevConfigRef.current === configKey) {
75
+ return;
76
+ }
77
+ prevConfigRef.current = configKey;
78
+ const newAuth = new Auth({
79
+ clientId: config.clientId,
80
+ redirectUri: config.redirectUri,
81
+ logoutRedirectUri: config.logoutRedirectUri,
82
+ audience: config.audience || DEFAULT_AUDIENCE,
83
+ scope: config.scope || DEFAULT_SCOPE,
84
+ authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN
85
+ });
86
+ setAuth(newAuth);
87
+ setIsAuthReady(true);
88
+ }, [config]);
89
+ useEffect(() => {
90
+ if (!auth || !isAuthReady) return;
91
+ if (session?.error) return;
92
+ if (!session?.accessToken || !session?.idToken) return;
93
+ const hydrateAuth = async () => {
94
+ try {
95
+ const {
96
+ accessToken,
97
+ idToken,
98
+ refreshToken,
99
+ accessTokenExpires
100
+ } = session;
101
+ if (!accessToken || !idToken) return;
102
+ const existingUser = await auth.getUser();
103
+ if (existingUser) return;
104
+ const expiresIn = accessTokenExpires ? Math.max(0, Math.floor((accessTokenExpires - Date.now()) / 1e3)) : DEFAULT_TOKEN_EXPIRY_SECONDS;
105
+ const tokenResponse = {
106
+ access_token: accessToken,
107
+ refresh_token: refreshToken,
108
+ id_token: idToken,
109
+ token_type: "Bearer",
110
+ expires_in: expiresIn
111
+ };
112
+ await auth.storeTokens(tokenResponse);
113
+ } catch (error) {
114
+ console.warn("[auth-nextjs] Failed to hydrate Auth instance:", error);
115
+ }
116
+ };
117
+ hydrateAuth();
118
+ }, [auth, isAuthReady, session]);
119
+ useEffect(() => {
120
+ if (!auth || !isAuthReady) return void 0;
121
+ const handleLoggedIn = async (authUser) => {
122
+ if (session?.accessToken && authUser.accessToken !== session.accessToken) {
123
+ await updateSession({
124
+ accessToken: authUser.accessToken,
125
+ refreshToken: authUser.refreshToken,
126
+ idToken: authUser.idToken,
127
+ accessTokenExpires: getTokenExpiry(authUser.accessToken),
128
+ zkEvm: authUser.zkEvm
129
+ });
130
+ }
131
+ };
132
+ auth.eventEmitter.on("loggedIn", handleLoggedIn);
133
+ return () => {
134
+ auth.eventEmitter.removeListener("loggedIn", handleLoggedIn);
135
+ };
136
+ }, [auth, isAuthReady, session, updateSession]);
137
+ const contextValue = useMemo(
138
+ () => ({ auth, config, basePath }),
139
+ [auth, config, basePath]
140
+ );
141
+ return /* @__PURE__ */ jsx(ImmutableAuthContext.Provider, { value: contextValue, children });
142
+ }
143
+ function ImmutableAuthProvider({
144
+ children,
145
+ config,
146
+ session,
147
+ basePath = DEFAULT_NEXTAUTH_BASE_PATH
148
+ }) {
149
+ return /* @__PURE__ */ jsx(SessionProvider, { session, basePath, children: /* @__PURE__ */ jsx(ImmutableAuthInner, { config, basePath, children }) });
150
+ }
151
+ function useImmutableAuth() {
152
+ const context = useContext(ImmutableAuthContext);
153
+ const { data: sessionData, status } = useSession();
154
+ if (!context) {
155
+ throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");
156
+ }
157
+ const session = sessionData;
158
+ const { auth } = context;
159
+ const isLoading = status === "loading";
160
+ const isAuthenticated = status === "authenticated" && !!session;
161
+ const user = session?.user ? {
162
+ sub: session.user.sub,
163
+ email: session.user.email,
164
+ nickname: session.user.nickname
165
+ } : null;
166
+ const handleSignIn = useCallback(async (options) => {
167
+ if (!auth) {
168
+ throw new Error("Auth not initialized");
169
+ }
170
+ const authUser = await auth.login(options);
171
+ if (!authUser) {
172
+ throw new Error("Login failed");
173
+ }
174
+ const tokenData = {
175
+ accessToken: authUser.accessToken,
176
+ refreshToken: authUser.refreshToken,
177
+ idToken: authUser.idToken,
178
+ accessTokenExpires: getTokenExpiry(authUser.accessToken),
179
+ profile: {
180
+ sub: authUser.profile.sub,
181
+ email: authUser.profile.email,
182
+ nickname: authUser.profile.nickname
183
+ },
184
+ zkEvm: authUser.zkEvm
185
+ };
186
+ const result = await signIn(IMMUTABLE_PROVIDER_ID, {
187
+ tokens: JSON.stringify(tokenData),
188
+ redirect: false
189
+ });
190
+ if (result?.error) {
191
+ throw new Error(`NextAuth sign-in failed: ${result.error}`);
192
+ }
193
+ if (!result?.ok) {
194
+ throw new Error("NextAuth sign-in failed: unknown error");
195
+ }
196
+ }, [auth]);
197
+ const handleSignOut = useCallback(async () => {
198
+ await signOut({ redirect: false });
199
+ if (auth) {
200
+ try {
201
+ await auth.getLogoutUrl();
202
+ } catch (error) {
203
+ console.warn("[auth-nextjs] Logout cleanup error:", error);
204
+ }
205
+ }
206
+ }, [auth]);
207
+ const getAccessToken = useCallback(async () => {
208
+ if (auth) {
209
+ try {
210
+ const token = await auth.getAccessToken();
211
+ if (token) {
212
+ return token;
213
+ }
214
+ } catch {
215
+ }
216
+ }
217
+ if (session?.error) {
218
+ throw new Error(`Token refresh failed: ${session.error}`);
219
+ }
220
+ if (session?.accessToken) {
221
+ return session.accessToken;
222
+ }
223
+ throw new Error("No access token available");
224
+ }, [auth, session]);
225
+ return {
226
+ user,
227
+ session,
228
+ isLoading,
229
+ isAuthenticated,
230
+ signIn: handleSignIn,
231
+ signOut: handleSignOut,
232
+ getAccessToken,
233
+ auth
234
+ };
235
+ }
236
+ function useAccessToken() {
237
+ const { getAccessToken } = useImmutableAuth();
238
+ return getAccessToken;
239
+ }
240
+
241
+ // src/client/callback.tsx
242
+ import { useEffect as useEffect2, useState as useState2, useRef as useRef2 } from "react";
243
+ import { useRouter } from "next/router";
244
+ import { signIn as signIn2 } from "next-auth/react";
245
+ import { Auth as Auth2 } from "@imtbl/auth";
246
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
247
+ function CallbackPage({
248
+ config,
249
+ redirectTo = "/",
250
+ loadingComponent = null,
251
+ errorComponent
252
+ }) {
253
+ const router = useRouter();
254
+ const [error, setError] = useState2(null);
255
+ const callbackProcessedRef = useRef2(false);
256
+ useEffect2(() => {
257
+ const handleCallback = async () => {
258
+ try {
259
+ const auth = new Auth2({
260
+ clientId: config.clientId,
261
+ redirectUri: config.redirectUri,
262
+ logoutRedirectUri: config.logoutRedirectUri,
263
+ audience: config.audience || DEFAULT_AUDIENCE,
264
+ scope: config.scope || DEFAULT_SCOPE,
265
+ authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN
266
+ });
267
+ const authUser = await auth.loginCallback();
268
+ if (window.opener) {
269
+ if (!authUser) {
270
+ throw new Error("Authentication failed: no user data received from login callback");
271
+ }
272
+ window.close();
273
+ } else if (authUser) {
274
+ const tokenData = {
275
+ accessToken: authUser.accessToken,
276
+ refreshToken: authUser.refreshToken,
277
+ idToken: authUser.idToken,
278
+ accessTokenExpires: getTokenExpiry(authUser.accessToken),
279
+ profile: {
280
+ sub: authUser.profile.sub,
281
+ email: authUser.profile.email,
282
+ nickname: authUser.profile.nickname
283
+ },
284
+ zkEvm: authUser.zkEvm
285
+ };
286
+ const result = await signIn2(IMMUTABLE_PROVIDER_ID, {
287
+ tokens: JSON.stringify(tokenData),
288
+ redirect: false
289
+ });
290
+ if (result?.error) {
291
+ throw new Error(`NextAuth sign-in failed: ${result.error}`);
292
+ }
293
+ if (!result?.ok) {
294
+ throw new Error("NextAuth sign-in failed: unknown error");
295
+ }
296
+ router.replace(redirectTo);
297
+ } else {
298
+ throw new Error("Authentication failed: no user data received from login callback");
299
+ }
300
+ } catch (err) {
301
+ setError(err instanceof Error ? err.message : "Authentication failed");
302
+ }
303
+ };
304
+ const handleOAuthError = () => {
305
+ const errorCode = router.query.error;
306
+ const errorDescription = router.query.error_description;
307
+ const errorMessage = errorDescription || errorCode || "Authentication failed";
308
+ setError(errorMessage);
309
+ };
310
+ if (!router.isReady) {
311
+ return;
312
+ }
313
+ if (router.query.error) {
314
+ handleOAuthError();
315
+ return;
316
+ }
317
+ if (router.query.code && !callbackProcessedRef.current) {
318
+ callbackProcessedRef.current = true;
319
+ handleCallback();
320
+ }
321
+ }, [
322
+ router.isReady,
323
+ router.query.code,
324
+ router.query.error,
325
+ router.query.error_description,
326
+ router,
327
+ config,
328
+ redirectTo
329
+ ]);
330
+ if (error) {
331
+ if (errorComponent) {
332
+ return errorComponent(error);
333
+ }
334
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem", textAlign: "center" }, children: [
335
+ /* @__PURE__ */ jsx2("h2", { style: { color: "#dc3545" }, children: "Authentication Error" }),
336
+ /* @__PURE__ */ jsx2("p", { children: error }),
337
+ /* @__PURE__ */ jsx2(
338
+ "button",
339
+ {
340
+ onClick: () => router.push("/"),
341
+ style: {
342
+ padding: "0.5rem 1rem",
343
+ marginTop: "1rem",
344
+ cursor: "pointer"
345
+ },
346
+ children: "Return to Home"
347
+ }
348
+ )
349
+ ] });
350
+ }
351
+ if (loadingComponent) {
352
+ return loadingComponent;
353
+ }
354
+ return /* @__PURE__ */ jsx2("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ jsx2("p", { children: "Completing authentication..." }) });
355
+ }
356
+
357
+ // src/client/index.ts
358
+ import { MarketingConsentStatus } from "@imtbl/auth";
359
+ export {
360
+ CallbackPage,
361
+ ImmutableAuthProvider,
362
+ MarketingConsentStatus,
363
+ useAccessToken,
364
+ useImmutableAuth
365
+ };
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ ImmutableAuth: () => ImmutableAuth,
34
+ MarketingConsentStatus: () => import_auth.MarketingConsentStatus,
35
+ isTokenExpired: () => isTokenExpired,
36
+ refreshAccessToken: () => refreshAccessToken
37
+ });
38
+ module.exports = __toCommonJS(src_exports);
39
+ var import_next_auth = __toESM(require("next-auth"), 1);
40
+
41
+ // src/config.ts
42
+ var import_credentials = __toESM(require("next-auth/providers/credentials"), 1);
43
+
44
+ // src/constants.ts
45
+ var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
46
+ var IMMUTABLE_PROVIDER_ID = "immutable";
47
+ var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
48
+ var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
49
+ var TOKEN_EXPIRY_BUFFER_SECONDS = 60;
50
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
51
+
52
+ // src/refresh.ts
53
+ async function refreshAccessToken(token, config) {
54
+ const authDomain = config.authenticationDomain || DEFAULT_AUTH_DOMAIN;
55
+ if (!token.refreshToken) {
56
+ return {
57
+ ...token,
58
+ error: "NoRefreshToken"
59
+ };
60
+ }
61
+ try {
62
+ const response = await fetch(`${authDomain}/oauth/token`, {
63
+ method: "POST",
64
+ headers: {
65
+ "Content-Type": "application/x-www-form-urlencoded"
66
+ },
67
+ body: new URLSearchParams({
68
+ grant_type: "refresh_token",
69
+ client_id: config.clientId,
70
+ refresh_token: token.refreshToken
71
+ })
72
+ });
73
+ if (!response.ok) {
74
+ let errorMessage = `Token refresh failed with status ${response.status}`;
75
+ try {
76
+ const errorData = await response.json();
77
+ if (errorData.error_description || errorData.error) {
78
+ errorMessage = errorData.error_description || errorData.error;
79
+ }
80
+ } catch {
81
+ }
82
+ throw new Error(errorMessage);
83
+ }
84
+ const data = await response.json();
85
+ if (!data.access_token || typeof data.access_token !== "string") {
86
+ throw new Error("Invalid token response: missing access_token");
87
+ }
88
+ const expiresIn = data.expires_in || DEFAULT_TOKEN_EXPIRY_SECONDS;
89
+ const accessTokenExpires = Date.now() + expiresIn * 1e3;
90
+ return {
91
+ ...token,
92
+ accessToken: data.access_token,
93
+ refreshToken: data.refresh_token ?? token.refreshToken,
94
+ idToken: data.id_token ?? token.idToken,
95
+ accessTokenExpires,
96
+ error: void 0
97
+ };
98
+ } catch (error) {
99
+ console.error("[auth-nextjs] Failed to refresh token:", error);
100
+ return {
101
+ ...token,
102
+ error: "RefreshTokenError"
103
+ };
104
+ }
105
+ }
106
+ function isTokenExpired(accessTokenExpires, bufferSeconds = TOKEN_EXPIRY_BUFFER_SECONDS) {
107
+ if (typeof accessTokenExpires !== "number" || Number.isNaN(accessTokenExpires)) {
108
+ return true;
109
+ }
110
+ return Date.now() >= accessTokenExpires - bufferSeconds * 1e3;
111
+ }
112
+
113
+ // src/config.ts
114
+ var CredentialsProvider = import_credentials.default.default || import_credentials.default;
115
+ async function validateTokens(accessToken, authDomain) {
116
+ try {
117
+ const response = await fetch(`${authDomain}/userinfo`, {
118
+ method: "GET",
119
+ headers: {
120
+ Authorization: `Bearer ${accessToken}`
121
+ }
122
+ });
123
+ if (!response.ok) {
124
+ console.error("[auth-nextjs] Token validation failed:", response.status, response.statusText);
125
+ return null;
126
+ }
127
+ return await response.json();
128
+ } catch (error) {
129
+ console.error("[auth-nextjs] Token validation error:", error);
130
+ return null;
131
+ }
132
+ }
133
+ function createAuthOptions(config) {
134
+ const authDomain = config.authenticationDomain || DEFAULT_AUTH_DOMAIN;
135
+ return {
136
+ providers: [
137
+ CredentialsProvider({
138
+ id: IMMUTABLE_PROVIDER_ID,
139
+ name: "Immutable",
140
+ credentials: {
141
+ tokens: { label: "Tokens", type: "text" }
142
+ },
143
+ async authorize(credentials) {
144
+ if (!credentials?.tokens) {
145
+ return null;
146
+ }
147
+ let tokenData;
148
+ try {
149
+ tokenData = JSON.parse(credentials.tokens);
150
+ } catch (error) {
151
+ console.error("[auth-nextjs] Failed to parse token data:", error);
152
+ return null;
153
+ }
154
+ if (!tokenData.accessToken || typeof tokenData.accessToken !== "string" || !tokenData.profile || typeof tokenData.profile !== "object" || !tokenData.profile.sub || typeof tokenData.profile.sub !== "string" || typeof tokenData.accessTokenExpires !== "number" || Number.isNaN(tokenData.accessTokenExpires)) {
155
+ console.error("[auth-nextjs] Invalid token data structure - missing required fields");
156
+ return null;
157
+ }
158
+ const userInfo = await validateTokens(tokenData.accessToken, authDomain);
159
+ if (!userInfo) {
160
+ console.error("[auth-nextjs] Token validation failed - rejecting authentication");
161
+ return null;
162
+ }
163
+ if (userInfo.sub !== tokenData.profile.sub) {
164
+ console.error(
165
+ "[auth-nextjs] User ID mismatch - userinfo sub:",
166
+ userInfo.sub,
167
+ "provided sub:",
168
+ tokenData.profile.sub
169
+ );
170
+ return null;
171
+ }
172
+ return {
173
+ id: userInfo.sub,
174
+ sub: userInfo.sub,
175
+ email: userInfo.email ?? tokenData.profile.email,
176
+ nickname: userInfo.nickname ?? tokenData.profile.nickname,
177
+ accessToken: tokenData.accessToken,
178
+ refreshToken: tokenData.refreshToken,
179
+ idToken: tokenData.idToken,
180
+ accessTokenExpires: tokenData.accessTokenExpires,
181
+ zkEvm: tokenData.zkEvm
182
+ };
183
+ }
184
+ })
185
+ ],
186
+ callbacks: {
187
+ async jwt({
188
+ token,
189
+ user,
190
+ trigger,
191
+ session: sessionUpdate
192
+ }) {
193
+ if (user) {
194
+ return {
195
+ ...token,
196
+ sub: user.sub,
197
+ email: user.email,
198
+ nickname: user.nickname,
199
+ accessToken: user.accessToken,
200
+ refreshToken: user.refreshToken,
201
+ idToken: user.idToken,
202
+ accessTokenExpires: user.accessTokenExpires,
203
+ zkEvm: user.zkEvm
204
+ };
205
+ }
206
+ if (trigger === "update" && sessionUpdate) {
207
+ return {
208
+ ...token,
209
+ ...sessionUpdate.accessToken && { accessToken: sessionUpdate.accessToken },
210
+ ...sessionUpdate.refreshToken && { refreshToken: sessionUpdate.refreshToken },
211
+ ...sessionUpdate.idToken && { idToken: sessionUpdate.idToken },
212
+ ...sessionUpdate.accessTokenExpires && { accessTokenExpires: sessionUpdate.accessTokenExpires },
213
+ ...sessionUpdate.zkEvm && { zkEvm: sessionUpdate.zkEvm }
214
+ };
215
+ }
216
+ if (!isTokenExpired(token.accessTokenExpires)) {
217
+ return token;
218
+ }
219
+ return refreshAccessToken(token, config);
220
+ },
221
+ async session({ session, token }) {
222
+ return {
223
+ ...session,
224
+ user: {
225
+ sub: token.sub,
226
+ email: token.email,
227
+ nickname: token.nickname
228
+ },
229
+ accessToken: token.accessToken,
230
+ refreshToken: token.refreshToken,
231
+ idToken: token.idToken,
232
+ accessTokenExpires: token.accessTokenExpires,
233
+ zkEvm: token.zkEvm,
234
+ ...token.error && { error: token.error }
235
+ };
236
+ }
237
+ },
238
+ session: {
239
+ strategy: "jwt",
240
+ // Session max age in seconds (30 days default)
241
+ maxAge: DEFAULT_SESSION_MAX_AGE_SECONDS
242
+ },
243
+ // Use NEXTAUTH_SECRET from environment
244
+ secret: process.env.NEXTAUTH_SECRET
245
+ };
246
+ }
247
+
248
+ // src/index.ts
249
+ var import_auth = require("@imtbl/auth");
250
+ var NextAuth = import_next_auth.default.default || import_next_auth.default;
251
+ function ImmutableAuth(config, overrides) {
252
+ const authOptions = createAuthOptions(config);
253
+ if (!overrides) {
254
+ return NextAuth(authOptions);
255
+ }
256
+ const composedCallbacks = {
257
+ ...authOptions.callbacks
258
+ };
259
+ if (overrides.callbacks) {
260
+ if (overrides.callbacks.jwt) {
261
+ const internalJwt = authOptions.callbacks?.jwt;
262
+ const userJwt = overrides.callbacks.jwt;
263
+ composedCallbacks.jwt = async (params) => {
264
+ const token = internalJwt ? await internalJwt(params) : params.token;
265
+ return userJwt({ ...params, token });
266
+ };
267
+ }
268
+ if (overrides.callbacks.session) {
269
+ const internalSession = authOptions.callbacks?.session;
270
+ const userSession = overrides.callbacks.session;
271
+ composedCallbacks.session = async (params) => {
272
+ const session = internalSession ? await internalSession(params) : params.session;
273
+ return userSession({ ...params, session });
274
+ };
275
+ }
276
+ if (overrides.callbacks.signIn) {
277
+ composedCallbacks.signIn = overrides.callbacks.signIn;
278
+ }
279
+ if (overrides.callbacks.redirect) {
280
+ composedCallbacks.redirect = overrides.callbacks.redirect;
281
+ }
282
+ }
283
+ const mergedOptions = {
284
+ ...authOptions,
285
+ ...overrides,
286
+ callbacks: composedCallbacks
287
+ };
288
+ return NextAuth(mergedOptions);
289
+ }
290
+ // Annotate the CommonJS export names for ESM import in node:
291
+ 0 && (module.exports = {
292
+ ImmutableAuth,
293
+ MarketingConsentStatus,
294
+ isTokenExpired,
295
+ refreshAccessToken
296
+ });