@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.
- package/dist/browser/index.js +10 -0
- package/dist/node/index.cjs +15 -497
- package/dist/node/index.js +8 -484
- package/package.json +11 -9
- /package/dist/{node → types}/callback.d.ts +0 -0
- /package/dist/{node → types}/constants.d.ts +0 -0
- /package/dist/{node → types}/index.d.ts +0 -0
- /package/dist/{node → types}/provider.d.ts +0 -0
- /package/dist/{node → types}/types.d.ts +0 -0
- /package/dist/{node → types}/utils/token.d.ts +0 -0
|
@@ -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 };
|
package/dist/node/index.cjs
CHANGED
|
@@ -1,501 +1,19 @@
|
|
|
1
|
-
'use
|
|
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
|
-
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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;
|
package/dist/node/index.js
CHANGED
|
@@ -1,486 +1,10 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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/
|
|
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.
|
|
31
|
-
"@imtbl/auth-next-server": "2.12.5-alpha.
|
|
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": "
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
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
|