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