@imtbl/auth-next-client 2.12.5-alpha.13 → 2.12.5-alpha.14

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,10 @@
1
+ import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from 'react';
2
+ import { SessionProvider, useSession, signIn, signOut } from 'next-auth/react';
3
+ import { decodeJwtPayload, Auth, AuthEvents } from '@imtbl/auth';
4
+ export { MarketingConsentStatus } from '@imtbl/auth';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { useRouter } from 'next/navigation';
7
+
8
+ var b="https://auth.immutable.com",w="platform_api",U="openid profile email offline_access transact",C="immutable",H="/api/auth";function I(s){if(!s)return Date.now()+9e5;try{let e=decodeJwtPayload(s);return e.exp&&typeof e.exp=="number"?e.exp*1e3:Date.now()+9e5}catch{return Date.now()+9e5}}var z=createContext(null);function W({children:s,config:e,basePath:d}){let[i,a]=useState(null),t=useRef(null),r=useRef(null),[f,k]=useState(!1),{data:l,update:h}=useSession();useEffect(()=>{if(typeof window>"u")return;let E=[e.clientId,e.redirectUri,e.popupRedirectUri||"",e.logoutRedirectUri||"",e.audience||w,e.scope||U,e.authenticationDomain||b,e.passportDomain||""].join(":");if(t.current===E&&r.current!==null)return;t.current=E;let u=new Auth({clientId:e.clientId,redirectUri:e.redirectUri,popupRedirectUri:e.popupRedirectUri,logoutRedirectUri:e.logoutRedirectUri,audience:e.audience||w,scope:e.scope||U,authenticationDomain:e.authenticationDomain||b,passportDomain:e.passportDomain});return r.current=u,a(u),k(!0),()=>{r.current=null,a(null),k(!1);}},[e]),useEffect(()=>{if(!i||!f)return;let E=async n=>{l?.accessToken&&n.accessToken!==l.accessToken&&await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},u=async n=>{await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},c=async n=>{console.warn("[auth-next-client] User removed from Auth SDK:",n.reason,n.error),await signOut({redirect:!1});},p=async()=>{await signOut({redirect:!1});};return i.eventEmitter.on(AuthEvents.LOGGED_IN,E),i.eventEmitter.on(AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.on(AuthEvents.USER_REMOVED,c),i.eventEmitter.on(AuthEvents.LOGGED_OUT,p),()=>{i.eventEmitter.removeListener(AuthEvents.LOGGED_IN,E),i.eventEmitter.removeListener(AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.removeListener(AuthEvents.USER_REMOVED,c),i.eventEmitter.removeListener(AuthEvents.LOGGED_OUT,p);}},[i,f,l,h]);let T=useMemo(()=>({auth:i,config:e,basePath:d}),[i,e,d]);return jsx(z.Provider,{value:T,children:s})}function Z({children:s,config:e,session:d,basePath:i=H}){return jsx(SessionProvider,{session:d,basePath:i,children:jsx(W,{config:e,basePath:i,children:s})})}function F(){let s=useContext(z),{data:e,status:d}=useSession(),[i,a]=useState(!1);if(!s)throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");let t=e,{auth:r}=s,f=d==="loading",k=d==="authenticated"&&!!t,l=t?.user?{sub:t.user.sub,email:t.user.email,nickname:t.user.nickname}:null,h=useCallback(async u=>{if(!r)throw new Error("Auth not initialized");a(!0);try{let c=await r.login(u);if(!c)throw new Error("Login failed");let p={accessToken:c.accessToken,refreshToken:c.refreshToken,idToken:c.idToken,accessTokenExpires:I(c.accessToken),profile:{sub:c.profile.sub,email:c.profile.email,nickname:c.profile.nickname},zkEvm:c.zkEvm},n=await signIn(C,{tokens:JSON.stringify(p),redirect:!1});if(n?.error)throw new Error(`NextAuth sign-in failed: ${n.error}`);if(!n?.ok)throw new Error("NextAuth sign-in failed: unknown error")}finally{a(!1);}},[r]),T=useCallback(async()=>{if(r)try{await r.getLogoutUrl();}catch(u){console.warn("[auth-next-client] Logout cleanup error:",u),await signOut({redirect:!1});}else await signOut({redirect:!1});},[r]),E=useCallback(async()=>{if(r)try{let u=await r.getAccessToken();if(u)return u}catch{}if(t?.error)throw new Error(t.error==="TokenExpired"?"Session expired. Please log in again.":`Authentication error: ${t.error}`);if(t?.accessToken)return t.accessToken;throw new Error("No access token available")},[r,t]);return {user:l,session:t,isLoading:f,isLoggingIn:i,isAuthenticated:k,signIn:h,signOut:T,getAccessToken:E,auth:r}}function q(){let{getAccessToken:s}=F();return s}function Q(s,e){let{getAccessToken:d,auth:i}=F(),{ssr:a,data:t,fetchError:r}=s,f=!a||!!r,[k,l]=useState(t),[h,T]=useState(f),[E,u]=useState(r?new Error(r):null),c=useRef(!1),p=useRef(0),n=useRef({serverData:t,ssr:a,fetchError:r});useEffect(()=>{let m=n.current;(m.serverData!==t||m.ssr!==a||m.fetchError!==r)&&(n.current={serverData:t,ssr:a,fetchError:r},c.current=!1,p.current+=1,a&&!r?(l(t),T(!1),u(null)):(l(null),T(!0),u(r?new Error(r):null)));},[t,a,r]);let o=useCallback(async()=>{let m=p.current;T(!0),u(null);try{let g=await d(),y=await e(g);p.current===m&&l(y);}catch(g){p.current===m&&u(g instanceof Error?g:new Error(String(g)));}finally{p.current===m&&T(!1);}},[e,d]);return useEffect(()=>{c.current||f&&(!a&&!i||(c.current=!0,o()));},[f,a,i,o]),{data:k,isLoading:h,error:E,refetch:o}}function se(){return typeof window>"u"?new URLSearchParams:new URLSearchParams(window.location.search)}function ie({config:s,redirectTo:e="/",loadingComponent:d=null,errorComponent:i,onSuccess:a,onError:t}){let r=useRouter(),[f,k]=useState(null),l=useRef(!1);return useEffect(()=>{let h=se(),T=async()=>{try{let o=await new Auth({clientId:s.clientId,redirectUri:s.redirectUri,popupRedirectUri:s.popupRedirectUri,logoutRedirectUri:s.logoutRedirectUri,audience:s.audience||w,scope:s.scope||U,authenticationDomain:s.authenticationDomain||b,passportDomain:s.passportDomain}).loginCallback();if(window.opener){if(!o)throw new Error("Authentication failed: no user data received from login callback");let m={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(m),window.close();}else if(o){let m={accessToken:o.accessToken,refreshToken:o.refreshToken,idToken:o.idToken,accessTokenExpires:I(o.accessToken),profile:{sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname},zkEvm:o.zkEvm},g=await signIn(C,{tokens:JSON.stringify(m),redirect:!1});if(g?.error)throw new Error(`NextAuth sign-in failed: ${g.error}`);if(!g?.ok)throw new Error("NextAuth sign-in failed: unknown error");let y={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(y);let G=typeof e=="function"?e(y)||"/":e;r.replace(G);}else throw new Error("Authentication failed: no user data received from login callback")}catch(n){let o=n instanceof Error?n.message:"Authentication failed";t&&t(o),k(o);}},E=()=>{let n=h.get("error"),m=h.get("error_description")||n||"Authentication failed";t&&t(m),k(m);};if(l.current)return;let u=h.get("error"),c=h.get("code");if(u){l.current=!0,E();return}if(c){l.current=!0,T();return}l.current=!0;let p="Invalid callback: missing OAuth parameters. Please try logging in again.";t&&t(p),k(p);},[r,s,e,a,t]),f?i?i(f):jsxs("div",{style:{padding:"2rem",textAlign:"center"},children:[jsx("h2",{style:{color:"#dc3545"},children:"Authentication Error"}),jsx("p",{children:f}),jsx("button",{onClick:()=>r.push("/"),type:"button",style:{padding:"0.5rem 1rem",marginTop:"1rem",cursor:"pointer"},children:"Return to Home"})]}):d||jsx("div",{style:{padding:"2rem",textAlign:"center"},children:jsx("p",{children:"Completing authentication..."})})}
9
+
10
+ export { ie as CallbackPage, Z as ImmutableAuthProvider, q as useAccessToken, Q as useHydratedData, F as useImmutableAuth };
@@ -1,501 +1,19 @@
1
- 'use client';
2
- "use strict";
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ 'use strict';
20
2
 
21
- // src/index.ts
22
- var src_exports = {};
23
- __export(src_exports, {
24
- CallbackPage: () => CallbackPage,
25
- ImmutableAuthProvider: () => ImmutableAuthProvider,
26
- MarketingConsentStatus: () => import_auth4.MarketingConsentStatus,
27
- useAccessToken: () => useAccessToken,
28
- useHydratedData: () => useHydratedData,
29
- useImmutableAuth: () => useImmutableAuth
30
- });
31
- module.exports = __toCommonJS(src_exports);
32
-
33
- // src/provider.tsx
34
- var import_react = require("react");
35
- var import_react2 = require("next-auth/react");
36
- var import_auth2 = require("@imtbl/auth");
37
-
38
- // src/utils/token.ts
39
- var import_auth = require("@imtbl/auth");
40
-
41
- // src/constants.ts
42
- var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
43
- var DEFAULT_AUDIENCE = "platform_api";
44
- var DEFAULT_SCOPE = "openid profile email offline_access transact";
45
- var IMMUTABLE_PROVIDER_ID = "immutable";
46
- var DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
47
- var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
48
- var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
49
-
50
- // src/utils/token.ts
51
- function getTokenExpiry(accessToken) {
52
- if (!accessToken) {
53
- return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
54
- }
55
- try {
56
- const payload = (0, import_auth.decodeJwtPayload)(accessToken);
57
- if (payload.exp && typeof payload.exp === "number") {
58
- return payload.exp * 1e3;
59
- }
60
- return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
61
- } catch {
62
- return Date.now() + DEFAULT_TOKEN_EXPIRY_MS;
63
- }
64
- }
65
-
66
- // src/provider.tsx
67
- var import_jsx_runtime = require("react/jsx-runtime");
68
- var ImmutableAuthContext = (0, import_react.createContext)(null);
69
- function ImmutableAuthInner({
70
- children,
71
- config,
72
- basePath
73
- }) {
74
- const [auth, setAuth] = (0, import_react.useState)(null);
75
- const prevConfigRef = (0, import_react.useRef)(null);
76
- const authInstanceRef = (0, import_react.useRef)(null);
77
- const [isAuthReady, setIsAuthReady] = (0, import_react.useState)(false);
78
- const { data: session, update: updateSession } = (0, import_react2.useSession)();
79
- (0, import_react.useEffect)(() => {
80
- if (typeof window === "undefined") return void 0;
81
- const configKey = [
82
- config.clientId,
83
- config.redirectUri,
84
- config.popupRedirectUri || "",
85
- config.logoutRedirectUri || "",
86
- config.audience || DEFAULT_AUDIENCE,
87
- config.scope || DEFAULT_SCOPE,
88
- config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
89
- config.passportDomain || ""
90
- ].join(":");
91
- if (prevConfigRef.current === configKey && authInstanceRef.current !== null) {
92
- return void 0;
93
- }
94
- prevConfigRef.current = configKey;
95
- const newAuth = new import_auth2.Auth({
96
- clientId: config.clientId,
97
- redirectUri: config.redirectUri,
98
- popupRedirectUri: config.popupRedirectUri,
99
- logoutRedirectUri: config.logoutRedirectUri,
100
- audience: config.audience || DEFAULT_AUDIENCE,
101
- scope: config.scope || DEFAULT_SCOPE,
102
- authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
103
- passportDomain: config.passportDomain
104
- });
105
- authInstanceRef.current = newAuth;
106
- setAuth(newAuth);
107
- setIsAuthReady(true);
108
- return () => {
109
- authInstanceRef.current = null;
110
- setAuth(null);
111
- setIsAuthReady(false);
112
- };
113
- }, [config]);
114
- (0, import_react.useEffect)(() => {
115
- if (!auth || !isAuthReady) return void 0;
116
- const handleLoggedIn = async (authUser) => {
117
- if (session?.accessToken && authUser.accessToken !== session.accessToken) {
118
- await updateSession({
119
- accessToken: authUser.accessToken,
120
- refreshToken: authUser.refreshToken,
121
- idToken: authUser.idToken,
122
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
123
- zkEvm: authUser.zkEvm
124
- });
125
- }
126
- };
127
- const handleTokenRefreshed = async (authUser) => {
128
- await updateSession({
129
- accessToken: authUser.accessToken,
130
- refreshToken: authUser.refreshToken,
131
- idToken: authUser.idToken,
132
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
133
- zkEvm: authUser.zkEvm
134
- });
135
- };
136
- const handleUserRemoved = async (payload) => {
137
- console.warn("[auth-next-client] User removed from Auth SDK:", payload.reason, payload.error);
138
- await (0, import_react2.signOut)({ redirect: false });
139
- };
140
- const handleLoggedOut = async () => {
141
- await (0, import_react2.signOut)({ redirect: false });
142
- };
143
- auth.eventEmitter.on(import_auth2.AuthEvents.LOGGED_IN, handleLoggedIn);
144
- auth.eventEmitter.on(import_auth2.AuthEvents.TOKEN_REFRESHED, handleTokenRefreshed);
145
- auth.eventEmitter.on(import_auth2.AuthEvents.USER_REMOVED, handleUserRemoved);
146
- auth.eventEmitter.on(import_auth2.AuthEvents.LOGGED_OUT, handleLoggedOut);
147
- return () => {
148
- auth.eventEmitter.removeListener(import_auth2.AuthEvents.LOGGED_IN, handleLoggedIn);
149
- auth.eventEmitter.removeListener(import_auth2.AuthEvents.TOKEN_REFRESHED, handleTokenRefreshed);
150
- auth.eventEmitter.removeListener(import_auth2.AuthEvents.USER_REMOVED, handleUserRemoved);
151
- auth.eventEmitter.removeListener(import_auth2.AuthEvents.LOGGED_OUT, handleLoggedOut);
152
- };
153
- }, [auth, isAuthReady, session, updateSession]);
154
- const contextValue = (0, import_react.useMemo)(
155
- () => ({ auth, config, basePath }),
156
- [auth, config, basePath]
157
- );
158
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImmutableAuthContext.Provider, { value: contextValue, children });
159
- }
160
- function ImmutableAuthProvider({
161
- children,
162
- config,
163
- session,
164
- basePath = DEFAULT_NEXTAUTH_BASE_PATH
165
- }) {
166
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.SessionProvider, { session, basePath, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImmutableAuthInner, { config, basePath, children }) });
167
- }
168
- function useImmutableAuth() {
169
- const context = (0, import_react.useContext)(ImmutableAuthContext);
170
- const { data: sessionData, status } = (0, import_react2.useSession)();
171
- const [isLoggingIn, setIsLoggingIn] = (0, import_react.useState)(false);
172
- if (!context) {
173
- throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");
174
- }
175
- const session = sessionData;
176
- const { auth } = context;
177
- const isLoading = status === "loading";
178
- const isAuthenticated = status === "authenticated" && !!session;
179
- const user = session?.user ? {
180
- sub: session.user.sub,
181
- email: session.user.email,
182
- nickname: session.user.nickname
183
- } : null;
184
- const handleSignIn = (0, import_react.useCallback)(async (options) => {
185
- if (!auth) {
186
- throw new Error("Auth not initialized");
187
- }
188
- setIsLoggingIn(true);
189
- try {
190
- const authUser = await auth.login(options);
191
- if (!authUser) {
192
- throw new Error("Login failed");
193
- }
194
- const tokenData = {
195
- accessToken: authUser.accessToken,
196
- refreshToken: authUser.refreshToken,
197
- idToken: authUser.idToken,
198
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
199
- profile: {
200
- sub: authUser.profile.sub,
201
- email: authUser.profile.email,
202
- nickname: authUser.profile.nickname
203
- },
204
- zkEvm: authUser.zkEvm
205
- };
206
- const result = await (0, import_react2.signIn)(IMMUTABLE_PROVIDER_ID, {
207
- tokens: JSON.stringify(tokenData),
208
- redirect: false
209
- });
210
- if (result?.error) {
211
- throw new Error(`NextAuth sign-in failed: ${result.error}`);
212
- }
213
- if (!result?.ok) {
214
- throw new Error("NextAuth sign-in failed: unknown error");
215
- }
216
- } finally {
217
- setIsLoggingIn(false);
218
- }
219
- }, [auth]);
220
- const handleSignOut = (0, import_react.useCallback)(async () => {
221
- if (auth) {
222
- try {
223
- await auth.getLogoutUrl();
224
- } catch (error) {
225
- console.warn("[auth-next-client] Logout cleanup error:", error);
226
- await (0, import_react2.signOut)({ redirect: false });
227
- }
228
- } else {
229
- await (0, import_react2.signOut)({ redirect: false });
230
- }
231
- }, [auth]);
232
- const getAccessToken = (0, import_react.useCallback)(async () => {
233
- if (auth) {
234
- try {
235
- const token = await auth.getAccessToken();
236
- if (token) {
237
- return token;
238
- }
239
- } catch {
240
- }
241
- }
242
- if (session?.error) {
243
- throw new Error(
244
- session.error === "TokenExpired" ? "Session expired. Please log in again." : `Authentication error: ${session.error}`
245
- );
246
- }
247
- if (session?.accessToken) {
248
- return session.accessToken;
249
- }
250
- throw new Error("No access token available");
251
- }, [auth, session]);
252
- return {
253
- user,
254
- session,
255
- isLoading,
256
- isLoggingIn,
257
- isAuthenticated,
258
- signIn: handleSignIn,
259
- signOut: handleSignOut,
260
- getAccessToken,
261
- auth
262
- };
263
- }
264
- function useAccessToken() {
265
- const { getAccessToken } = useImmutableAuth();
266
- return getAccessToken;
267
- }
268
- function useHydratedData(props, fetcher) {
269
- const { getAccessToken, auth } = useImmutableAuth();
270
- const {
271
- ssr,
272
- data: serverData,
273
- fetchError
274
- } = props;
275
- const needsClientFetch = !ssr || Boolean(fetchError);
276
- const [data, setData] = (0, import_react.useState)(serverData);
277
- const [isLoading, setIsLoading] = (0, import_react.useState)(needsClientFetch);
278
- const [error, setError] = (0, import_react.useState)(
279
- fetchError ? new Error(fetchError) : null
280
- );
281
- const hasFetchedRef = (0, import_react.useRef)(false);
282
- const fetchIdRef = (0, import_react.useRef)(0);
283
- const prevPropsRef = (0, import_react.useRef)({ serverData, ssr, fetchError });
284
- (0, import_react.useEffect)(() => {
285
- const prevProps = prevPropsRef.current;
286
- const propsChanged = prevProps.serverData !== serverData || prevProps.ssr !== ssr || prevProps.fetchError !== fetchError;
287
- if (propsChanged) {
288
- prevPropsRef.current = { serverData, ssr, fetchError };
289
- hasFetchedRef.current = false;
290
- fetchIdRef.current += 1;
291
- if (ssr && !fetchError) {
292
- setData(serverData);
293
- setIsLoading(false);
294
- setError(null);
295
- } else {
296
- setData(null);
297
- setIsLoading(true);
298
- setError(fetchError ? new Error(fetchError) : null);
299
- }
300
- }
301
- }, [serverData, ssr, fetchError]);
302
- const fetchData = (0, import_react.useCallback)(async () => {
303
- const currentFetchId = fetchIdRef.current;
304
- setIsLoading(true);
305
- setError(null);
306
- try {
307
- const token = await getAccessToken();
308
- const result = await fetcher(token);
309
- if (fetchIdRef.current === currentFetchId) {
310
- setData(result);
311
- }
312
- } catch (err) {
313
- if (fetchIdRef.current === currentFetchId) {
314
- setError(err instanceof Error ? err : new Error(String(err)));
315
- }
316
- } finally {
317
- if (fetchIdRef.current === currentFetchId) {
318
- setIsLoading(false);
319
- }
320
- }
321
- }, [fetcher, getAccessToken]);
322
- (0, import_react.useEffect)(() => {
323
- if (hasFetchedRef.current) return;
324
- if (!needsClientFetch) return;
325
- if (!ssr && !auth) return;
326
- hasFetchedRef.current = true;
327
- fetchData();
328
- }, [needsClientFetch, ssr, auth, fetchData]);
329
- return {
330
- data,
331
- isLoading,
332
- error,
333
- refetch: fetchData
334
- };
335
- }
3
+ var react = require('react');
4
+ var react$1 = require('next-auth/react');
5
+ var auth = require('@imtbl/auth');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var navigation = require('next/navigation');
336
8
 
337
- // src/callback.tsx
338
- var import_react3 = require("react");
339
- var import_navigation = require("next/navigation");
340
- var import_react4 = require("next-auth/react");
341
- var import_auth3 = require("@imtbl/auth");
342
- var import_jsx_runtime2 = require("react/jsx-runtime");
343
- function getSearchParams() {
344
- if (typeof window === "undefined") {
345
- return new URLSearchParams();
346
- }
347
- return new URLSearchParams(window.location.search);
348
- }
349
- function CallbackPage({
350
- config,
351
- redirectTo = "/",
352
- loadingComponent = null,
353
- errorComponent,
354
- onSuccess,
355
- onError
356
- }) {
357
- const router = (0, import_navigation.useRouter)();
358
- const [error, setError] = (0, import_react3.useState)(null);
359
- const callbackProcessedRef = (0, import_react3.useRef)(false);
360
- (0, import_react3.useEffect)(() => {
361
- const searchParams = getSearchParams();
362
- const handleCallback = async () => {
363
- try {
364
- const auth = new import_auth3.Auth({
365
- clientId: config.clientId,
366
- redirectUri: config.redirectUri,
367
- popupRedirectUri: config.popupRedirectUri,
368
- logoutRedirectUri: config.logoutRedirectUri,
369
- audience: config.audience || DEFAULT_AUDIENCE,
370
- scope: config.scope || DEFAULT_SCOPE,
371
- authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
372
- passportDomain: config.passportDomain
373
- });
374
- const authUser = await auth.loginCallback();
375
- if (window.opener) {
376
- if (!authUser) {
377
- throw new Error("Authentication failed: no user data received from login callback");
378
- }
379
- const user = {
380
- sub: authUser.profile.sub,
381
- email: authUser.profile.email,
382
- nickname: authUser.profile.nickname
383
- };
384
- if (onSuccess) {
385
- await onSuccess(user);
386
- }
387
- window.close();
388
- } else if (authUser) {
389
- const tokenData = {
390
- accessToken: authUser.accessToken,
391
- refreshToken: authUser.refreshToken,
392
- idToken: authUser.idToken,
393
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
394
- profile: {
395
- sub: authUser.profile.sub,
396
- email: authUser.profile.email,
397
- nickname: authUser.profile.nickname
398
- },
399
- zkEvm: authUser.zkEvm
400
- };
401
- const result = await (0, import_react4.signIn)(IMMUTABLE_PROVIDER_ID, {
402
- tokens: JSON.stringify(tokenData),
403
- redirect: false
404
- });
405
- if (result?.error) {
406
- throw new Error(`NextAuth sign-in failed: ${result.error}`);
407
- }
408
- if (!result?.ok) {
409
- throw new Error("NextAuth sign-in failed: unknown error");
410
- }
411
- const user = {
412
- sub: authUser.profile.sub,
413
- email: authUser.profile.email,
414
- nickname: authUser.profile.nickname
415
- };
416
- if (onSuccess) {
417
- await onSuccess(user);
418
- }
419
- const resolvedRedirectTo = typeof redirectTo === "function" ? redirectTo(user) || "/" : redirectTo;
420
- router.replace(resolvedRedirectTo);
421
- } else {
422
- throw new Error("Authentication failed: no user data received from login callback");
423
- }
424
- } catch (err) {
425
- const errorMessage2 = err instanceof Error ? err.message : "Authentication failed";
426
- if (onError) {
427
- onError(errorMessage2);
428
- }
429
- setError(errorMessage2);
430
- }
431
- };
432
- const handleOAuthError = () => {
433
- const errorCode = searchParams.get("error");
434
- const errorDescription = searchParams.get("error_description");
435
- const errorMessage2 = errorDescription || errorCode || "Authentication failed";
436
- if (onError) {
437
- onError(errorMessage2);
438
- }
439
- setError(errorMessage2);
440
- };
441
- if (callbackProcessedRef.current) {
442
- return;
443
- }
444
- const hasError = searchParams.get("error");
445
- const hasCode = searchParams.get("code");
446
- if (hasError) {
447
- callbackProcessedRef.current = true;
448
- handleOAuthError();
449
- return;
450
- }
451
- if (hasCode) {
452
- callbackProcessedRef.current = true;
453
- handleCallback();
454
- return;
455
- }
456
- callbackProcessedRef.current = true;
457
- const errorMessage = "Invalid callback: missing OAuth parameters. Please try logging in again.";
458
- if (onError) {
459
- onError(errorMessage);
460
- }
461
- setError(errorMessage);
462
- }, [router, config, redirectTo, onSuccess, onError]);
463
- if (error) {
464
- if (errorComponent) {
465
- return errorComponent(error);
466
- }
467
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "2rem", textAlign: "center" }, children: [
468
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: { color: "#dc3545" }, children: "Authentication Error" }),
469
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: error }),
470
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
471
- "button",
472
- {
473
- onClick: () => router.push("/"),
474
- type: "button",
475
- style: {
476
- padding: "0.5rem 1rem",
477
- marginTop: "1rem",
478
- cursor: "pointer"
479
- },
480
- children: "Return to Home"
481
- }
482
- )
483
- ] });
484
- }
485
- if (loadingComponent) {
486
- return loadingComponent;
487
- }
488
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "Completing authentication..." }) });
489
- }
9
+ var b="https://auth.immutable.com",w="platform_api",U="openid profile email offline_access transact",v="immutable",N="/api/auth";function I(s){if(!s)return Date.now()+9e5;try{let e=auth.decodeJwtPayload(s);return e.exp&&typeof e.exp=="number"?e.exp*1e3:Date.now()+9e5}catch{return Date.now()+9e5}}var H=react.createContext(null);function Y({children:s,config:e,basePath:d}){let[i,a]=react.useState(null),t=react.useRef(null),r=react.useRef(null),[f,k]=react.useState(!1),{data:l,update:h}=react$1.useSession();react.useEffect(()=>{if(typeof window>"u")return;let E=[e.clientId,e.redirectUri,e.popupRedirectUri||"",e.logoutRedirectUri||"",e.audience||w,e.scope||U,e.authenticationDomain||b,e.passportDomain||""].join(":");if(t.current===E&&r.current!==null)return;t.current=E;let u=new auth.Auth({clientId:e.clientId,redirectUri:e.redirectUri,popupRedirectUri:e.popupRedirectUri,logoutRedirectUri:e.logoutRedirectUri,audience:e.audience||w,scope:e.scope||U,authenticationDomain:e.authenticationDomain||b,passportDomain:e.passportDomain});return r.current=u,a(u),k(!0),()=>{r.current=null,a(null),k(!1);}},[e]),react.useEffect(()=>{if(!i||!f)return;let E=async n=>{l?.accessToken&&n.accessToken!==l.accessToken&&await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},u=async n=>{await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},c=async n=>{console.warn("[auth-next-client] User removed from Auth SDK:",n.reason,n.error),await react$1.signOut({redirect:!1});},p=async()=>{await react$1.signOut({redirect:!1});};return i.eventEmitter.on(auth.AuthEvents.LOGGED_IN,E),i.eventEmitter.on(auth.AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.on(auth.AuthEvents.USER_REMOVED,c),i.eventEmitter.on(auth.AuthEvents.LOGGED_OUT,p),()=>{i.eventEmitter.removeListener(auth.AuthEvents.LOGGED_IN,E),i.eventEmitter.removeListener(auth.AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.removeListener(auth.AuthEvents.USER_REMOVED,c),i.eventEmitter.removeListener(auth.AuthEvents.LOGGED_OUT,p);}},[i,f,l,h]);let T=react.useMemo(()=>({auth:i,config:e,basePath:d}),[i,e,d]);return jsxRuntime.jsx(H.Provider,{value:T,children:s})}function $({children:s,config:e,session:d,basePath:i=N}){return jsxRuntime.jsx(react$1.SessionProvider,{session:d,basePath:i,children:jsxRuntime.jsx(Y,{config:e,basePath:i,children:s})})}function S(){let s=react.useContext(H),{data:e,status:d}=react$1.useSession(),[i,a]=react.useState(!1);if(!s)throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");let t=e,{auth:r}=s,f=d==="loading",k=d==="authenticated"&&!!t,l=t?.user?{sub:t.user.sub,email:t.user.email,nickname:t.user.nickname}:null,h=react.useCallback(async u=>{if(!r)throw new Error("Auth not initialized");a(!0);try{let c=await r.login(u);if(!c)throw new Error("Login failed");let p={accessToken:c.accessToken,refreshToken:c.refreshToken,idToken:c.idToken,accessTokenExpires:I(c.accessToken),profile:{sub:c.profile.sub,email:c.profile.email,nickname:c.profile.nickname},zkEvm:c.zkEvm},n=await react$1.signIn(v,{tokens:JSON.stringify(p),redirect:!1});if(n?.error)throw new Error(`NextAuth sign-in failed: ${n.error}`);if(!n?.ok)throw new Error("NextAuth sign-in failed: unknown error")}finally{a(!1);}},[r]),T=react.useCallback(async()=>{if(r)try{await r.getLogoutUrl();}catch(u){console.warn("[auth-next-client] Logout cleanup error:",u),await react$1.signOut({redirect:!1});}else await react$1.signOut({redirect:!1});},[r]),E=react.useCallback(async()=>{if(r)try{let u=await r.getAccessToken();if(u)return u}catch{}if(t?.error)throw new Error(t.error==="TokenExpired"?"Session expired. Please log in again.":`Authentication error: ${t.error}`);if(t?.accessToken)return t.accessToken;throw new Error("No access token available")},[r,t]);return {user:l,session:t,isLoading:f,isLoggingIn:i,isAuthenticated:k,signIn:h,signOut:T,getAccessToken:E,auth:r}}function W(){let{getAccessToken:s}=S();return s}function Z(s,e){let{getAccessToken:d,auth:i}=S(),{ssr:a,data:t,fetchError:r}=s,f=!a||!!r,[k,l]=react.useState(t),[h,T]=react.useState(f),[E,u]=react.useState(r?new Error(r):null),c=react.useRef(!1),p=react.useRef(0),n=react.useRef({serverData:t,ssr:a,fetchError:r});react.useEffect(()=>{let m=n.current;(m.serverData!==t||m.ssr!==a||m.fetchError!==r)&&(n.current={serverData:t,ssr:a,fetchError:r},c.current=!1,p.current+=1,a&&!r?(l(t),T(!1),u(null)):(l(null),T(!0),u(r?new Error(r):null)));},[t,a,r]);let o=react.useCallback(async()=>{let m=p.current;T(!0),u(null);try{let g=await d(),y=await e(g);p.current===m&&l(y);}catch(g){p.current===m&&u(g instanceof Error?g:new Error(String(g)));}finally{p.current===m&&T(!1);}},[e,d]);return react.useEffect(()=>{c.current||f&&(!a&&!i||(c.current=!0,o()));},[f,a,i,o]),{data:k,isLoading:h,error:E,refetch:o}}function ne(){return typeof window>"u"?new URLSearchParams:new URLSearchParams(window.location.search)}function oe({config:s,redirectTo:e="/",loadingComponent:d=null,errorComponent:i,onSuccess:a,onError:t}){let r=navigation.useRouter(),[f,k]=react.useState(null),l=react.useRef(!1);return react.useEffect(()=>{let h=ne(),T=async()=>{try{let o=await new auth.Auth({clientId:s.clientId,redirectUri:s.redirectUri,popupRedirectUri:s.popupRedirectUri,logoutRedirectUri:s.logoutRedirectUri,audience:s.audience||w,scope:s.scope||U,authenticationDomain:s.authenticationDomain||b,passportDomain:s.passportDomain}).loginCallback();if(window.opener){if(!o)throw new Error("Authentication failed: no user data received from login callback");let m={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(m),window.close();}else if(o){let m={accessToken:o.accessToken,refreshToken:o.refreshToken,idToken:o.idToken,accessTokenExpires:I(o.accessToken),profile:{sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname},zkEvm:o.zkEvm},g=await react$1.signIn(v,{tokens:JSON.stringify(m),redirect:!1});if(g?.error)throw new Error(`NextAuth sign-in failed: ${g.error}`);if(!g?.ok)throw new Error("NextAuth sign-in failed: unknown error");let y={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(y);let M=typeof e=="function"?e(y)||"/":e;r.replace(M);}else throw new Error("Authentication failed: no user data received from login callback")}catch(n){let o=n instanceof Error?n.message:"Authentication failed";t&&t(o),k(o);}},E=()=>{let n=h.get("error"),m=h.get("error_description")||n||"Authentication failed";t&&t(m),k(m);};if(l.current)return;let u=h.get("error"),c=h.get("code");if(u){l.current=!0,E();return}if(c){l.current=!0,T();return}l.current=!0;let p="Invalid callback: missing OAuth parameters. Please try logging in again.";t&&t(p),k(p);},[r,s,e,a,t]),f?i?i(f):jsxRuntime.jsxs("div",{style:{padding:"2rem",textAlign:"center"},children:[jsxRuntime.jsx("h2",{style:{color:"#dc3545"},children:"Authentication Error"}),jsxRuntime.jsx("p",{children:f}),jsxRuntime.jsx("button",{onClick:()=>r.push("/"),type:"button",style:{padding:"0.5rem 1rem",marginTop:"1rem",cursor:"pointer"},children:"Return to Home"})]}):d||jsxRuntime.jsx("div",{style:{padding:"2rem",textAlign:"center"},children:jsxRuntime.jsx("p",{children:"Completing authentication..."})})}
490
10
 
491
- // src/index.ts
492
- var import_auth4 = require("@imtbl/auth");
493
- // Annotate the CommonJS export names for ESM import in node:
494
- 0 && (module.exports = {
495
- CallbackPage,
496
- ImmutableAuthProvider,
497
- MarketingConsentStatus,
498
- useAccessToken,
499
- useHydratedData,
500
- useImmutableAuth
11
+ Object.defineProperty(exports, "MarketingConsentStatus", {
12
+ enumerable: true,
13
+ get: function () { return auth.MarketingConsentStatus; }
501
14
  });
15
+ exports.CallbackPage = oe;
16
+ exports.ImmutableAuthProvider = $;
17
+ exports.useAccessToken = W;
18
+ exports.useHydratedData = Z;
19
+ exports.useImmutableAuth = S;
@@ -1,486 +1,10 @@
1
- 'use client';
1
+ import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from 'react';
2
+ import { SessionProvider, useSession, signIn, signOut } from 'next-auth/react';
3
+ import { decodeJwtPayload, Auth, AuthEvents } from '@imtbl/auth';
4
+ export { MarketingConsentStatus } from '@imtbl/auth';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { useRouter } from 'next/navigation';
2
7
 
3
- // src/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
- AuthEvents
22
- } from "@imtbl/auth";
8
+ var b="https://auth.immutable.com",w="platform_api",U="openid profile email offline_access transact",v="immutable",N="/api/auth";function I(s){if(!s)return Date.now()+9e5;try{let e=decodeJwtPayload(s);return e.exp&&typeof e.exp=="number"?e.exp*1e3:Date.now()+9e5}catch{return Date.now()+9e5}}var H=createContext(null);function Y({children:s,config:e,basePath:d}){let[i,a]=useState(null),t=useRef(null),r=useRef(null),[f,k]=useState(!1),{data:l,update:h}=useSession();useEffect(()=>{if(typeof window>"u")return;let E=[e.clientId,e.redirectUri,e.popupRedirectUri||"",e.logoutRedirectUri||"",e.audience||w,e.scope||U,e.authenticationDomain||b,e.passportDomain||""].join(":");if(t.current===E&&r.current!==null)return;t.current=E;let u=new Auth({clientId:e.clientId,redirectUri:e.redirectUri,popupRedirectUri:e.popupRedirectUri,logoutRedirectUri:e.logoutRedirectUri,audience:e.audience||w,scope:e.scope||U,authenticationDomain:e.authenticationDomain||b,passportDomain:e.passportDomain});return r.current=u,a(u),k(!0),()=>{r.current=null,a(null),k(!1);}},[e]),useEffect(()=>{if(!i||!f)return;let E=async n=>{l?.accessToken&&n.accessToken!==l.accessToken&&await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},u=async n=>{await h({accessToken:n.accessToken,refreshToken:n.refreshToken,idToken:n.idToken,accessTokenExpires:I(n.accessToken),zkEvm:n.zkEvm});},c=async n=>{console.warn("[auth-next-client] User removed from Auth SDK:",n.reason,n.error),await signOut({redirect:!1});},p=async()=>{await signOut({redirect:!1});};return i.eventEmitter.on(AuthEvents.LOGGED_IN,E),i.eventEmitter.on(AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.on(AuthEvents.USER_REMOVED,c),i.eventEmitter.on(AuthEvents.LOGGED_OUT,p),()=>{i.eventEmitter.removeListener(AuthEvents.LOGGED_IN,E),i.eventEmitter.removeListener(AuthEvents.TOKEN_REFRESHED,u),i.eventEmitter.removeListener(AuthEvents.USER_REMOVED,c),i.eventEmitter.removeListener(AuthEvents.LOGGED_OUT,p);}},[i,f,l,h]);let T=useMemo(()=>({auth:i,config:e,basePath:d}),[i,e,d]);return jsx(H.Provider,{value:T,children:s})}function $({children:s,config:e,session:d,basePath:i=N}){return jsx(SessionProvider,{session:d,basePath:i,children:jsx(Y,{config:e,basePath:i,children:s})})}function S(){let s=useContext(H),{data:e,status:d}=useSession(),[i,a]=useState(!1);if(!s)throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");let t=e,{auth:r}=s,f=d==="loading",k=d==="authenticated"&&!!t,l=t?.user?{sub:t.user.sub,email:t.user.email,nickname:t.user.nickname}:null,h=useCallback(async u=>{if(!r)throw new Error("Auth not initialized");a(!0);try{let c=await r.login(u);if(!c)throw new Error("Login failed");let p={accessToken:c.accessToken,refreshToken:c.refreshToken,idToken:c.idToken,accessTokenExpires:I(c.accessToken),profile:{sub:c.profile.sub,email:c.profile.email,nickname:c.profile.nickname},zkEvm:c.zkEvm},n=await signIn(v,{tokens:JSON.stringify(p),redirect:!1});if(n?.error)throw new Error(`NextAuth sign-in failed: ${n.error}`);if(!n?.ok)throw new Error("NextAuth sign-in failed: unknown error")}finally{a(!1);}},[r]),T=useCallback(async()=>{if(r)try{await r.getLogoutUrl();}catch(u){console.warn("[auth-next-client] Logout cleanup error:",u),await signOut({redirect:!1});}else await signOut({redirect:!1});},[r]),E=useCallback(async()=>{if(r)try{let u=await r.getAccessToken();if(u)return u}catch{}if(t?.error)throw new Error(t.error==="TokenExpired"?"Session expired. Please log in again.":`Authentication error: ${t.error}`);if(t?.accessToken)return t.accessToken;throw new Error("No access token available")},[r,t]);return {user:l,session:t,isLoading:f,isLoggingIn:i,isAuthenticated:k,signIn:h,signOut:T,getAccessToken:E,auth:r}}function W(){let{getAccessToken:s}=S();return s}function Z(s,e){let{getAccessToken:d,auth:i}=S(),{ssr:a,data:t,fetchError:r}=s,f=!a||!!r,[k,l]=useState(t),[h,T]=useState(f),[E,u]=useState(r?new Error(r):null),c=useRef(!1),p=useRef(0),n=useRef({serverData:t,ssr:a,fetchError:r});useEffect(()=>{let m=n.current;(m.serverData!==t||m.ssr!==a||m.fetchError!==r)&&(n.current={serverData:t,ssr:a,fetchError:r},c.current=!1,p.current+=1,a&&!r?(l(t),T(!1),u(null)):(l(null),T(!0),u(r?new Error(r):null)));},[t,a,r]);let o=useCallback(async()=>{let m=p.current;T(!0),u(null);try{let g=await d(),y=await e(g);p.current===m&&l(y);}catch(g){p.current===m&&u(g instanceof Error?g:new Error(String(g)));}finally{p.current===m&&T(!1);}},[e,d]);return useEffect(()=>{c.current||f&&(!a&&!i||(c.current=!0,o()));},[f,a,i,o]),{data:k,isLoading:h,error:E,refetch:o}}function ne(){return typeof window>"u"?new URLSearchParams:new URLSearchParams(window.location.search)}function oe({config:s,redirectTo:e="/",loadingComponent:d=null,errorComponent:i,onSuccess:a,onError:t}){let r=useRouter(),[f,k]=useState(null),l=useRef(!1);return useEffect(()=>{let h=ne(),T=async()=>{try{let o=await new Auth({clientId:s.clientId,redirectUri:s.redirectUri,popupRedirectUri:s.popupRedirectUri,logoutRedirectUri:s.logoutRedirectUri,audience:s.audience||w,scope:s.scope||U,authenticationDomain:s.authenticationDomain||b,passportDomain:s.passportDomain}).loginCallback();if(window.opener){if(!o)throw new Error("Authentication failed: no user data received from login callback");let m={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(m),window.close();}else if(o){let m={accessToken:o.accessToken,refreshToken:o.refreshToken,idToken:o.idToken,accessTokenExpires:I(o.accessToken),profile:{sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname},zkEvm:o.zkEvm},g=await signIn(v,{tokens:JSON.stringify(m),redirect:!1});if(g?.error)throw new Error(`NextAuth sign-in failed: ${g.error}`);if(!g?.ok)throw new Error("NextAuth sign-in failed: unknown error");let y={sub:o.profile.sub,email:o.profile.email,nickname:o.profile.nickname};a&&await a(y);let M=typeof e=="function"?e(y)||"/":e;r.replace(M);}else throw new Error("Authentication failed: no user data received from login callback")}catch(n){let o=n instanceof Error?n.message:"Authentication failed";t&&t(o),k(o);}},E=()=>{let n=h.get("error"),m=h.get("error_description")||n||"Authentication failed";t&&t(m),k(m);};if(l.current)return;let u=h.get("error"),c=h.get("code");if(u){l.current=!0,E();return}if(c){l.current=!0,T();return}l.current=!0;let p="Invalid callback: missing OAuth parameters. Please try logging in again.";t&&t(p),k(p);},[r,s,e,a,t]),f?i?i(f):jsxs("div",{style:{padding:"2rem",textAlign:"center"},children:[jsx("h2",{style:{color:"#dc3545"},children:"Authentication Error"}),jsx("p",{children:f}),jsx("button",{onClick:()=>r.push("/"),type:"button",style:{padding:"0.5rem 1rem",marginTop:"1rem",cursor:"pointer"},children:"Return to Home"})]}):d||jsx("div",{style:{padding:"2rem",textAlign:"center"},children:jsx("p",{children:"Completing authentication..."})})}
23
9
 
24
- // src/utils/token.ts
25
- import { decodeJwtPayload } from "@imtbl/auth";
26
-
27
- // src/constants.ts
28
- var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
29
- var DEFAULT_AUDIENCE = "platform_api";
30
- var DEFAULT_SCOPE = "openid profile email offline_access transact";
31
- var IMMUTABLE_PROVIDER_ID = "immutable";
32
- var DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
33
- var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
34
- var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
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/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 authInstanceRef = useRef(null);
63
- const [isAuthReady, setIsAuthReady] = useState(false);
64
- const { data: session, update: updateSession } = useSession();
65
- useEffect(() => {
66
- if (typeof window === "undefined") return void 0;
67
- const configKey = [
68
- config.clientId,
69
- config.redirectUri,
70
- config.popupRedirectUri || "",
71
- config.logoutRedirectUri || "",
72
- config.audience || DEFAULT_AUDIENCE,
73
- config.scope || DEFAULT_SCOPE,
74
- config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
75
- config.passportDomain || ""
76
- ].join(":");
77
- if (prevConfigRef.current === configKey && authInstanceRef.current !== null) {
78
- return void 0;
79
- }
80
- prevConfigRef.current = configKey;
81
- const newAuth = new Auth({
82
- clientId: config.clientId,
83
- redirectUri: config.redirectUri,
84
- popupRedirectUri: config.popupRedirectUri,
85
- logoutRedirectUri: config.logoutRedirectUri,
86
- audience: config.audience || DEFAULT_AUDIENCE,
87
- scope: config.scope || DEFAULT_SCOPE,
88
- authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
89
- passportDomain: config.passportDomain
90
- });
91
- authInstanceRef.current = newAuth;
92
- setAuth(newAuth);
93
- setIsAuthReady(true);
94
- return () => {
95
- authInstanceRef.current = null;
96
- setAuth(null);
97
- setIsAuthReady(false);
98
- };
99
- }, [config]);
100
- useEffect(() => {
101
- if (!auth || !isAuthReady) return void 0;
102
- const handleLoggedIn = async (authUser) => {
103
- if (session?.accessToken && authUser.accessToken !== session.accessToken) {
104
- await updateSession({
105
- accessToken: authUser.accessToken,
106
- refreshToken: authUser.refreshToken,
107
- idToken: authUser.idToken,
108
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
109
- zkEvm: authUser.zkEvm
110
- });
111
- }
112
- };
113
- const handleTokenRefreshed = async (authUser) => {
114
- await updateSession({
115
- accessToken: authUser.accessToken,
116
- refreshToken: authUser.refreshToken,
117
- idToken: authUser.idToken,
118
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
119
- zkEvm: authUser.zkEvm
120
- });
121
- };
122
- const handleUserRemoved = async (payload) => {
123
- console.warn("[auth-next-client] User removed from Auth SDK:", payload.reason, payload.error);
124
- await signOut({ redirect: false });
125
- };
126
- const handleLoggedOut = async () => {
127
- await signOut({ redirect: false });
128
- };
129
- auth.eventEmitter.on(AuthEvents.LOGGED_IN, handleLoggedIn);
130
- auth.eventEmitter.on(AuthEvents.TOKEN_REFRESHED, handleTokenRefreshed);
131
- auth.eventEmitter.on(AuthEvents.USER_REMOVED, handleUserRemoved);
132
- auth.eventEmitter.on(AuthEvents.LOGGED_OUT, handleLoggedOut);
133
- return () => {
134
- auth.eventEmitter.removeListener(AuthEvents.LOGGED_IN, handleLoggedIn);
135
- auth.eventEmitter.removeListener(AuthEvents.TOKEN_REFRESHED, handleTokenRefreshed);
136
- auth.eventEmitter.removeListener(AuthEvents.USER_REMOVED, handleUserRemoved);
137
- auth.eventEmitter.removeListener(AuthEvents.LOGGED_OUT, handleLoggedOut);
138
- };
139
- }, [auth, isAuthReady, session, updateSession]);
140
- const contextValue = useMemo(
141
- () => ({ auth, config, basePath }),
142
- [auth, config, basePath]
143
- );
144
- return /* @__PURE__ */ jsx(ImmutableAuthContext.Provider, { value: contextValue, children });
145
- }
146
- function ImmutableAuthProvider({
147
- children,
148
- config,
149
- session,
150
- basePath = DEFAULT_NEXTAUTH_BASE_PATH
151
- }) {
152
- return /* @__PURE__ */ jsx(SessionProvider, { session, basePath, children: /* @__PURE__ */ jsx(ImmutableAuthInner, { config, basePath, children }) });
153
- }
154
- function useImmutableAuth() {
155
- const context = useContext(ImmutableAuthContext);
156
- const { data: sessionData, status } = useSession();
157
- const [isLoggingIn, setIsLoggingIn] = useState(false);
158
- if (!context) {
159
- throw new Error("useImmutableAuth must be used within ImmutableAuthProvider");
160
- }
161
- const session = sessionData;
162
- const { auth } = context;
163
- const isLoading = status === "loading";
164
- const isAuthenticated = status === "authenticated" && !!session;
165
- const user = session?.user ? {
166
- sub: session.user.sub,
167
- email: session.user.email,
168
- nickname: session.user.nickname
169
- } : null;
170
- const handleSignIn = useCallback(async (options) => {
171
- if (!auth) {
172
- throw new Error("Auth not initialized");
173
- }
174
- setIsLoggingIn(true);
175
- try {
176
- const authUser = await auth.login(options);
177
- if (!authUser) {
178
- throw new Error("Login failed");
179
- }
180
- const tokenData = {
181
- accessToken: authUser.accessToken,
182
- refreshToken: authUser.refreshToken,
183
- idToken: authUser.idToken,
184
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
185
- profile: {
186
- sub: authUser.profile.sub,
187
- email: authUser.profile.email,
188
- nickname: authUser.profile.nickname
189
- },
190
- zkEvm: authUser.zkEvm
191
- };
192
- const result = await signIn(IMMUTABLE_PROVIDER_ID, {
193
- tokens: JSON.stringify(tokenData),
194
- redirect: false
195
- });
196
- if (result?.error) {
197
- throw new Error(`NextAuth sign-in failed: ${result.error}`);
198
- }
199
- if (!result?.ok) {
200
- throw new Error("NextAuth sign-in failed: unknown error");
201
- }
202
- } finally {
203
- setIsLoggingIn(false);
204
- }
205
- }, [auth]);
206
- const handleSignOut = useCallback(async () => {
207
- if (auth) {
208
- try {
209
- await auth.getLogoutUrl();
210
- } catch (error) {
211
- console.warn("[auth-next-client] Logout cleanup error:", error);
212
- await signOut({ redirect: false });
213
- }
214
- } else {
215
- await signOut({ redirect: false });
216
- }
217
- }, [auth]);
218
- const getAccessToken = useCallback(async () => {
219
- if (auth) {
220
- try {
221
- const token = await auth.getAccessToken();
222
- if (token) {
223
- return token;
224
- }
225
- } catch {
226
- }
227
- }
228
- if (session?.error) {
229
- throw new Error(
230
- session.error === "TokenExpired" ? "Session expired. Please log in again." : `Authentication error: ${session.error}`
231
- );
232
- }
233
- if (session?.accessToken) {
234
- return session.accessToken;
235
- }
236
- throw new Error("No access token available");
237
- }, [auth, session]);
238
- return {
239
- user,
240
- session,
241
- isLoading,
242
- isLoggingIn,
243
- isAuthenticated,
244
- signIn: handleSignIn,
245
- signOut: handleSignOut,
246
- getAccessToken,
247
- auth
248
- };
249
- }
250
- function useAccessToken() {
251
- const { getAccessToken } = useImmutableAuth();
252
- return getAccessToken;
253
- }
254
- function useHydratedData(props, fetcher) {
255
- const { getAccessToken, auth } = useImmutableAuth();
256
- const {
257
- ssr,
258
- data: serverData,
259
- fetchError
260
- } = props;
261
- const needsClientFetch = !ssr || Boolean(fetchError);
262
- const [data, setData] = useState(serverData);
263
- const [isLoading, setIsLoading] = useState(needsClientFetch);
264
- const [error, setError] = useState(
265
- fetchError ? new Error(fetchError) : null
266
- );
267
- const hasFetchedRef = useRef(false);
268
- const fetchIdRef = useRef(0);
269
- const prevPropsRef = useRef({ serverData, ssr, fetchError });
270
- useEffect(() => {
271
- const prevProps = prevPropsRef.current;
272
- const propsChanged = prevProps.serverData !== serverData || prevProps.ssr !== ssr || prevProps.fetchError !== fetchError;
273
- if (propsChanged) {
274
- prevPropsRef.current = { serverData, ssr, fetchError };
275
- hasFetchedRef.current = false;
276
- fetchIdRef.current += 1;
277
- if (ssr && !fetchError) {
278
- setData(serverData);
279
- setIsLoading(false);
280
- setError(null);
281
- } else {
282
- setData(null);
283
- setIsLoading(true);
284
- setError(fetchError ? new Error(fetchError) : null);
285
- }
286
- }
287
- }, [serverData, ssr, fetchError]);
288
- const fetchData = useCallback(async () => {
289
- const currentFetchId = fetchIdRef.current;
290
- setIsLoading(true);
291
- setError(null);
292
- try {
293
- const token = await getAccessToken();
294
- const result = await fetcher(token);
295
- if (fetchIdRef.current === currentFetchId) {
296
- setData(result);
297
- }
298
- } catch (err) {
299
- if (fetchIdRef.current === currentFetchId) {
300
- setError(err instanceof Error ? err : new Error(String(err)));
301
- }
302
- } finally {
303
- if (fetchIdRef.current === currentFetchId) {
304
- setIsLoading(false);
305
- }
306
- }
307
- }, [fetcher, getAccessToken]);
308
- useEffect(() => {
309
- if (hasFetchedRef.current) return;
310
- if (!needsClientFetch) return;
311
- if (!ssr && !auth) return;
312
- hasFetchedRef.current = true;
313
- fetchData();
314
- }, [needsClientFetch, ssr, auth, fetchData]);
315
- return {
316
- data,
317
- isLoading,
318
- error,
319
- refetch: fetchData
320
- };
321
- }
322
-
323
- // src/callback.tsx
324
- import { useEffect as useEffect2, useState as useState2, useRef as useRef2 } from "react";
325
- import { useRouter } from "next/navigation";
326
- import { signIn as signIn2 } from "next-auth/react";
327
- import { Auth as Auth2 } from "@imtbl/auth";
328
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
329
- function getSearchParams() {
330
- if (typeof window === "undefined") {
331
- return new URLSearchParams();
332
- }
333
- return new URLSearchParams(window.location.search);
334
- }
335
- function CallbackPage({
336
- config,
337
- redirectTo = "/",
338
- loadingComponent = null,
339
- errorComponent,
340
- onSuccess,
341
- onError
342
- }) {
343
- const router = useRouter();
344
- const [error, setError] = useState2(null);
345
- const callbackProcessedRef = useRef2(false);
346
- useEffect2(() => {
347
- const searchParams = getSearchParams();
348
- const handleCallback = async () => {
349
- try {
350
- const auth = new Auth2({
351
- clientId: config.clientId,
352
- redirectUri: config.redirectUri,
353
- popupRedirectUri: config.popupRedirectUri,
354
- logoutRedirectUri: config.logoutRedirectUri,
355
- audience: config.audience || DEFAULT_AUDIENCE,
356
- scope: config.scope || DEFAULT_SCOPE,
357
- authenticationDomain: config.authenticationDomain || DEFAULT_AUTH_DOMAIN,
358
- passportDomain: config.passportDomain
359
- });
360
- const authUser = await auth.loginCallback();
361
- if (window.opener) {
362
- if (!authUser) {
363
- throw new Error("Authentication failed: no user data received from login callback");
364
- }
365
- const user = {
366
- sub: authUser.profile.sub,
367
- email: authUser.profile.email,
368
- nickname: authUser.profile.nickname
369
- };
370
- if (onSuccess) {
371
- await onSuccess(user);
372
- }
373
- window.close();
374
- } else if (authUser) {
375
- const tokenData = {
376
- accessToken: authUser.accessToken,
377
- refreshToken: authUser.refreshToken,
378
- idToken: authUser.idToken,
379
- accessTokenExpires: getTokenExpiry(authUser.accessToken),
380
- profile: {
381
- sub: authUser.profile.sub,
382
- email: authUser.profile.email,
383
- nickname: authUser.profile.nickname
384
- },
385
- zkEvm: authUser.zkEvm
386
- };
387
- const result = await signIn2(IMMUTABLE_PROVIDER_ID, {
388
- tokens: JSON.stringify(tokenData),
389
- redirect: false
390
- });
391
- if (result?.error) {
392
- throw new Error(`NextAuth sign-in failed: ${result.error}`);
393
- }
394
- if (!result?.ok) {
395
- throw new Error("NextAuth sign-in failed: unknown error");
396
- }
397
- const user = {
398
- sub: authUser.profile.sub,
399
- email: authUser.profile.email,
400
- nickname: authUser.profile.nickname
401
- };
402
- if (onSuccess) {
403
- await onSuccess(user);
404
- }
405
- const resolvedRedirectTo = typeof redirectTo === "function" ? redirectTo(user) || "/" : redirectTo;
406
- router.replace(resolvedRedirectTo);
407
- } else {
408
- throw new Error("Authentication failed: no user data received from login callback");
409
- }
410
- } catch (err) {
411
- const errorMessage2 = err instanceof Error ? err.message : "Authentication failed";
412
- if (onError) {
413
- onError(errorMessage2);
414
- }
415
- setError(errorMessage2);
416
- }
417
- };
418
- const handleOAuthError = () => {
419
- const errorCode = searchParams.get("error");
420
- const errorDescription = searchParams.get("error_description");
421
- const errorMessage2 = errorDescription || errorCode || "Authentication failed";
422
- if (onError) {
423
- onError(errorMessage2);
424
- }
425
- setError(errorMessage2);
426
- };
427
- if (callbackProcessedRef.current) {
428
- return;
429
- }
430
- const hasError = searchParams.get("error");
431
- const hasCode = searchParams.get("code");
432
- if (hasError) {
433
- callbackProcessedRef.current = true;
434
- handleOAuthError();
435
- return;
436
- }
437
- if (hasCode) {
438
- callbackProcessedRef.current = true;
439
- handleCallback();
440
- return;
441
- }
442
- callbackProcessedRef.current = true;
443
- const errorMessage = "Invalid callback: missing OAuth parameters. Please try logging in again.";
444
- if (onError) {
445
- onError(errorMessage);
446
- }
447
- setError(errorMessage);
448
- }, [router, config, redirectTo, onSuccess, onError]);
449
- if (error) {
450
- if (errorComponent) {
451
- return errorComponent(error);
452
- }
453
- return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem", textAlign: "center" }, children: [
454
- /* @__PURE__ */ jsx2("h2", { style: { color: "#dc3545" }, children: "Authentication Error" }),
455
- /* @__PURE__ */ jsx2("p", { children: error }),
456
- /* @__PURE__ */ jsx2(
457
- "button",
458
- {
459
- onClick: () => router.push("/"),
460
- type: "button",
461
- style: {
462
- padding: "0.5rem 1rem",
463
- marginTop: "1rem",
464
- cursor: "pointer"
465
- },
466
- children: "Return to Home"
467
- }
468
- )
469
- ] });
470
- }
471
- if (loadingComponent) {
472
- return loadingComponent;
473
- }
474
- return /* @__PURE__ */ jsx2("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ jsx2("p", { children: "Completing authentication..." }) });
475
- }
476
-
477
- // src/index.ts
478
- import { MarketingConsentStatus } from "@imtbl/auth";
479
- export {
480
- CallbackPage,
481
- ImmutableAuthProvider,
482
- MarketingConsentStatus,
483
- useAccessToken,
484
- useHydratedData,
485
- useImmutableAuth
486
- };
10
+ export { oe as CallbackPage, $ as ImmutableAuthProvider, W as useAccessToken, Z as useHydratedData, S as useImmutableAuth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imtbl/auth-next-client",
3
- "version": "2.12.5-alpha.13",
3
+ "version": "2.12.5-alpha.14",
4
4
  "description": "Immutable Auth.js v5 integration for Next.js - Client-side components",
5
5
  "author": "Immutable",
6
6
  "license": "Apache-2.0",
@@ -11,7 +11,7 @@
11
11
  "type": "module",
12
12
  "main": "./dist/node/index.cjs",
13
13
  "module": "./dist/node/index.js",
14
- "types": "./dist/node/index.d.ts",
14
+ "types": "./dist/types/index.d.ts",
15
15
  "exports": {
16
16
  ".": {
17
17
  "development": {
@@ -20,15 +20,15 @@
20
20
  "default": "./dist/node/index.js"
21
21
  },
22
22
  "default": {
23
- "types": "./dist/node/index.d.ts",
23
+ "types": "./dist/types/index.d.ts",
24
24
  "require": "./dist/node/index.cjs",
25
25
  "default": "./dist/node/index.js"
26
26
  }
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
- "@imtbl/auth": "2.12.5-alpha.13",
31
- "@imtbl/auth-next-server": "2.12.5-alpha.13"
30
+ "@imtbl/auth": "2.12.5-alpha.14",
31
+ "@imtbl/auth-next-server": "2.12.5-alpha.14"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "next": "^14.2.0 || ^15.0.0",
@@ -61,10 +61,12 @@
61
61
  "typescript": "^5.6.2"
62
62
  },
63
63
  "scripts": {
64
- "build": "tsup && pnpm build:types",
65
- "build:types": "tsc --project tsconfig.types.json",
66
- "clean": "rm -rf dist",
67
- "lint": "eslint src/**/*.{ts,tsx} --max-warnings=0",
64
+ "build": "pnpm transpile && pnpm typegen",
65
+ "transpile": "tsup src/index.ts --config ../../tsup.config.js",
66
+ "typegen": "tsc --customConditions default --emitDeclarationOnly --outDir dist/types",
67
+ "pack:root": "pnpm pack --pack-destination $(dirname $(pnpm root -w))",
68
+ "lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0",
69
+ "typecheck": "tsc --customConditions default --noEmit --jsx preserve",
68
70
  "test": "jest --passWithNoTests"
69
71
  }
70
72
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes