@imtbl/auth-next-client 2.12.7-alpha.8 → 2.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,10 @@ npm install @imtbl/auth-next-client @imtbl/auth-next-server next-auth@5
27
27
  - `next` >= 14.0.0
28
28
  - `next-auth` >= 5.0.0-beta.25
29
29
 
30
+ ### Next.js 14 Compatibility
31
+
32
+ This package is compatible with both Next.js 14 and 15. It uses only standard APIs available in both versions (`next/navigation` for `useRouter`, `next-auth/react`). No Next.js 15-only APIs are used.
33
+
30
34
  ## Quick Start
31
35
 
32
36
  ### 1. Set Up Server-Side Auth
@@ -100,6 +104,42 @@ export default function Callback() {
100
104
  }
101
105
  ```
102
106
 
107
+ ### Default Auth (Zero Config)
108
+
109
+ When using `createAuthConfig()` with no args on the server, you can call login/logout with no config—sandbox clientId and redirectUri are used:
110
+
111
+ ```tsx
112
+ // With default auth - no config needed
113
+ function LoginButton() {
114
+ const { isAuthenticated } = useImmutableSession();
115
+ const { loginWithPopup, isLoggingIn, error } = useLogin();
116
+
117
+ if (isAuthenticated) return <p>You are logged in!</p>;
118
+
119
+ return (
120
+ <button onClick={() => loginWithPopup()} disabled={isLoggingIn}>
121
+ {isLoggingIn ? "Signing in..." : "Sign In"}
122
+ </button>
123
+ );
124
+ }
125
+ ```
126
+
127
+ Or with custom config (pass full LoginConfig/LogoutConfig when overriding):
128
+
129
+ ```tsx
130
+ // With custom config - pass complete config
131
+ loginWithPopup({
132
+ clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
133
+ redirectUri: `${window.location.origin}/callback`,
134
+ });
135
+ logout({
136
+ clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
137
+ logoutRedirectUri: process.env.NEXT_PUBLIC_BASE_URL!,
138
+ });
139
+ ```
140
+
141
+ See the [wallets-connect-with-nextjs](../../examples/passport/wallets-connect-with-nextjs) example for a full integration with `@imtbl/wallet`.
142
+
103
143
  ### 5. Add Login Button
104
144
 
105
145
  Use the `useLogin` hook for login flows with built-in state management:
@@ -22,7 +22,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  var src_exports = {};
23
23
  __export(src_exports, {
24
24
  CallbackPage: () => CallbackPage,
25
+ DEFAULT_AUDIENCE: () => DEFAULT_AUDIENCE,
26
+ DEFAULT_AUTH_DOMAIN: () => DEFAULT_AUTH_DOMAIN,
27
+ DEFAULT_LOGOUT_REDIRECT_URI_PATH: () => DEFAULT_LOGOUT_REDIRECT_URI_PATH,
28
+ DEFAULT_REDIRECT_URI_PATH: () => DEFAULT_REDIRECT_URI_PATH,
29
+ DEFAULT_SANDBOX_CLIENT_ID: () => DEFAULT_SANDBOX_CLIENT_ID,
30
+ DEFAULT_SCOPE: () => DEFAULT_SCOPE,
31
+ IMMUTABLE_PROVIDER_ID: () => IMMUTABLE_PROVIDER_ID,
25
32
  MarketingConsentStatus: () => import_auth3.MarketingConsentStatus,
33
+ deriveDefaultRedirectUri: () => deriveDefaultRedirectUri,
26
34
  useImmutableSession: () => useImmutableSession,
27
35
  useLogin: () => useLogin,
28
36
  useLogout: () => useLogout
@@ -36,10 +44,14 @@ var import_react2 = require("next-auth/react");
36
44
  var import_auth = require("@imtbl/auth");
37
45
 
38
46
  // src/constants.ts
47
+ var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
48
+ var DEFAULT_AUDIENCE = "platform_api";
49
+ var DEFAULT_SCOPE = "openid profile email offline_access transact";
39
50
  var IMMUTABLE_PROVIDER_ID = "immutable";
40
- var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
41
- var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
42
- var TOKEN_EXPIRY_BUFFER_MS = 60 * 1e3;
51
+ var DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
52
+ var DEFAULT_REDIRECT_URI_PATH = "/callback";
53
+ var DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
54
+ var TOKEN_EXPIRY_BUFFER_MS = 6e4;
43
55
 
44
56
  // src/idTokenStorage.ts
45
57
  var ID_TOKEN_STORAGE_KEY = "imtbl_id_token";
@@ -213,6 +225,18 @@ function CallbackPage({
213
225
  var import_react3 = require("react");
214
226
  var import_react4 = require("next-auth/react");
215
227
  var import_auth2 = require("@imtbl/auth");
228
+
229
+ // src/defaultConfig.ts
230
+ function deriveDefaultRedirectUri() {
231
+ if (typeof window === "undefined") {
232
+ throw new Error(
233
+ "[auth-next-client] deriveDefaultRedirectUri requires window. Login hooks run in the browser when the user triggers login."
234
+ );
235
+ }
236
+ return `${window.location.origin}${DEFAULT_REDIRECT_URI_PATH}`;
237
+ }
238
+
239
+ // src/hooks.tsx
216
240
  var pendingRefresh = null;
217
241
  function deduplicatedUpdate(update) {
218
242
  if (!pendingRefresh) {
@@ -222,6 +246,30 @@ function deduplicatedUpdate(update) {
222
246
  }
223
247
  return pendingRefresh;
224
248
  }
249
+ function getSandboxLoginConfig() {
250
+ const redirectUri = deriveDefaultRedirectUri();
251
+ return {
252
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
253
+ redirectUri,
254
+ popupRedirectUri: redirectUri,
255
+ scope: DEFAULT_SCOPE,
256
+ audience: DEFAULT_AUDIENCE,
257
+ authenticationDomain: DEFAULT_AUTH_DOMAIN
258
+ };
259
+ }
260
+ function getSandboxLogoutConfig() {
261
+ if (typeof window === "undefined") {
262
+ throw new Error(
263
+ "[auth-next-client] getSandboxLogoutConfig requires window. Logout runs in the browser when the user triggers it."
264
+ );
265
+ }
266
+ const logoutRedirectUri = window.location.origin + DEFAULT_LOGOUT_REDIRECT_URI_PATH;
267
+ return {
268
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
269
+ logoutRedirectUri,
270
+ authenticationDomain: DEFAULT_AUTH_DOMAIN
271
+ };
272
+ }
225
273
  function useImmutableSession() {
226
274
  const { data: sessionData, status, update } = (0, import_react4.useSession)();
227
275
  const [isRefreshing, setIsRefreshing] = (0, import_react3.useState)(false);
@@ -353,7 +401,8 @@ function useLogin() {
353
401
  setIsLoggingIn(true);
354
402
  setError(null);
355
403
  try {
356
- const tokens = await (0, import_auth2.loginWithPopup)(config, options);
404
+ const fullConfig = config ?? getSandboxLoginConfig();
405
+ const tokens = await (0, import_auth2.loginWithPopup)(fullConfig, options);
357
406
  await signInWithTokens(tokens);
358
407
  } catch (err) {
359
408
  const errorMessage = err instanceof Error ? err.message : "Login failed";
@@ -367,7 +416,8 @@ function useLogin() {
367
416
  setIsLoggingIn(true);
368
417
  setError(null);
369
418
  try {
370
- const tokens = await (0, import_auth2.loginWithEmbedded)(config);
419
+ const fullConfig = config ?? getSandboxLoginConfig();
420
+ const tokens = await (0, import_auth2.loginWithEmbedded)(fullConfig);
371
421
  await signInWithTokens(tokens);
372
422
  } catch (err) {
373
423
  const errorMessage = err instanceof Error ? err.message : "Login failed";
@@ -381,7 +431,8 @@ function useLogin() {
381
431
  setIsLoggingIn(true);
382
432
  setError(null);
383
433
  try {
384
- await (0, import_auth2.loginWithRedirect)(config, options);
434
+ const fullConfig = config ?? getSandboxLoginConfig();
435
+ await (0, import_auth2.loginWithRedirect)(fullConfig, options);
385
436
  } catch (err) {
386
437
  const errorMessage = err instanceof Error ? err.message : "Login failed";
387
438
  setError(errorMessage);
@@ -406,7 +457,8 @@ function useLogout() {
406
457
  try {
407
458
  clearStoredIdToken();
408
459
  await (0, import_react4.signOut)({ redirect: false });
409
- (0, import_auth2.logoutWithRedirect)(config);
460
+ const fullConfig = config ?? getSandboxLogoutConfig();
461
+ (0, import_auth2.logoutWithRedirect)(fullConfig);
410
462
  } catch (err) {
411
463
  const errorMessage = err instanceof Error ? err.message : "Logout failed";
412
464
  setError(errorMessage);
@@ -426,7 +478,15 @@ var import_auth3 = require("@imtbl/auth");
426
478
  // Annotate the CommonJS export names for ESM import in node:
427
479
  0 && (module.exports = {
428
480
  CallbackPage,
481
+ DEFAULT_AUDIENCE,
482
+ DEFAULT_AUTH_DOMAIN,
483
+ DEFAULT_LOGOUT_REDIRECT_URI_PATH,
484
+ DEFAULT_REDIRECT_URI_PATH,
485
+ DEFAULT_SANDBOX_CLIENT_ID,
486
+ DEFAULT_SCOPE,
487
+ IMMUTABLE_PROVIDER_ID,
429
488
  MarketingConsentStatus,
489
+ deriveDefaultRedirectUri,
430
490
  useImmutableSession,
431
491
  useLogin,
432
492
  useLogout
@@ -7,10 +7,14 @@ import { signIn } from "next-auth/react";
7
7
  import { handleLoginCallback as handleAuthCallback } from "@imtbl/auth";
8
8
 
9
9
  // src/constants.ts
10
+ var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
11
+ var DEFAULT_AUDIENCE = "platform_api";
12
+ var DEFAULT_SCOPE = "openid profile email offline_access transact";
10
13
  var IMMUTABLE_PROVIDER_ID = "immutable";
11
- var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
12
- var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
13
- var TOKEN_EXPIRY_BUFFER_MS = 60 * 1e3;
14
+ var DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
15
+ var DEFAULT_REDIRECT_URI_PATH = "/callback";
16
+ var DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
17
+ var TOKEN_EXPIRY_BUFFER_MS = 6e4;
14
18
 
15
19
  // src/idTokenStorage.ts
16
20
  var ID_TOKEN_STORAGE_KEY = "imtbl_id_token";
@@ -194,6 +198,18 @@ import {
194
198
  loginWithRedirect as rawLoginWithRedirect,
195
199
  logoutWithRedirect as rawLogoutWithRedirect
196
200
  } from "@imtbl/auth";
201
+
202
+ // src/defaultConfig.ts
203
+ function deriveDefaultRedirectUri() {
204
+ if (typeof window === "undefined") {
205
+ throw new Error(
206
+ "[auth-next-client] deriveDefaultRedirectUri requires window. Login hooks run in the browser when the user triggers login."
207
+ );
208
+ }
209
+ return `${window.location.origin}${DEFAULT_REDIRECT_URI_PATH}`;
210
+ }
211
+
212
+ // src/hooks.tsx
197
213
  var pendingRefresh = null;
198
214
  function deduplicatedUpdate(update) {
199
215
  if (!pendingRefresh) {
@@ -203,6 +219,30 @@ function deduplicatedUpdate(update) {
203
219
  }
204
220
  return pendingRefresh;
205
221
  }
222
+ function getSandboxLoginConfig() {
223
+ const redirectUri = deriveDefaultRedirectUri();
224
+ return {
225
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
226
+ redirectUri,
227
+ popupRedirectUri: redirectUri,
228
+ scope: DEFAULT_SCOPE,
229
+ audience: DEFAULT_AUDIENCE,
230
+ authenticationDomain: DEFAULT_AUTH_DOMAIN
231
+ };
232
+ }
233
+ function getSandboxLogoutConfig() {
234
+ if (typeof window === "undefined") {
235
+ throw new Error(
236
+ "[auth-next-client] getSandboxLogoutConfig requires window. Logout runs in the browser when the user triggers it."
237
+ );
238
+ }
239
+ const logoutRedirectUri = window.location.origin + DEFAULT_LOGOUT_REDIRECT_URI_PATH;
240
+ return {
241
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
242
+ logoutRedirectUri,
243
+ authenticationDomain: DEFAULT_AUTH_DOMAIN
244
+ };
245
+ }
206
246
  function useImmutableSession() {
207
247
  const { data: sessionData, status, update } = useSession();
208
248
  const [isRefreshing, setIsRefreshing] = useState2(false);
@@ -334,7 +374,8 @@ function useLogin() {
334
374
  setIsLoggingIn(true);
335
375
  setError(null);
336
376
  try {
337
- const tokens = await rawLoginWithPopup(config, options);
377
+ const fullConfig = config ?? getSandboxLoginConfig();
378
+ const tokens = await rawLoginWithPopup(fullConfig, options);
338
379
  await signInWithTokens(tokens);
339
380
  } catch (err) {
340
381
  const errorMessage = err instanceof Error ? err.message : "Login failed";
@@ -348,7 +389,8 @@ function useLogin() {
348
389
  setIsLoggingIn(true);
349
390
  setError(null);
350
391
  try {
351
- const tokens = await rawLoginWithEmbedded(config);
392
+ const fullConfig = config ?? getSandboxLoginConfig();
393
+ const tokens = await rawLoginWithEmbedded(fullConfig);
352
394
  await signInWithTokens(tokens);
353
395
  } catch (err) {
354
396
  const errorMessage = err instanceof Error ? err.message : "Login failed";
@@ -362,7 +404,8 @@ function useLogin() {
362
404
  setIsLoggingIn(true);
363
405
  setError(null);
364
406
  try {
365
- await rawLoginWithRedirect(config, options);
407
+ const fullConfig = config ?? getSandboxLoginConfig();
408
+ await rawLoginWithRedirect(fullConfig, options);
366
409
  } catch (err) {
367
410
  const errorMessage = err instanceof Error ? err.message : "Login failed";
368
411
  setError(errorMessage);
@@ -387,7 +430,8 @@ function useLogout() {
387
430
  try {
388
431
  clearStoredIdToken();
389
432
  await signOut({ redirect: false });
390
- rawLogoutWithRedirect(config);
433
+ const fullConfig = config ?? getSandboxLogoutConfig();
434
+ rawLogoutWithRedirect(fullConfig);
391
435
  } catch (err) {
392
436
  const errorMessage = err instanceof Error ? err.message : "Logout failed";
393
437
  setError(errorMessage);
@@ -406,7 +450,15 @@ function useLogout() {
406
450
  import { MarketingConsentStatus } from "@imtbl/auth";
407
451
  export {
408
452
  CallbackPage,
453
+ DEFAULT_AUDIENCE,
454
+ DEFAULT_AUTH_DOMAIN,
455
+ DEFAULT_LOGOUT_REDIRECT_URI_PATH,
456
+ DEFAULT_REDIRECT_URI_PATH,
457
+ DEFAULT_SANDBOX_CLIENT_ID,
458
+ DEFAULT_SCOPE,
459
+ IMMUTABLE_PROVIDER_ID,
409
460
  MarketingConsentStatus,
461
+ deriveDefaultRedirectUri,
410
462
  useImmutableSession,
411
463
  useLogin,
412
464
  useLogout
@@ -1,37 +1,15 @@
1
1
  /**
2
- * Shared constants for @imtbl/auth-next-client
3
- */
4
- /**
5
- * Default Immutable authentication domain
2
+ * Client-side constants for @imtbl/auth-next-client.
3
+ * Defined locally to avoid importing from auth-next-server (which uses next/server).
4
+ * Values must stay in sync with auth-next-server constants.
6
5
  */
7
6
  export declare const DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
8
- /**
9
- * Default OAuth audience
10
- */
11
7
  export declare const DEFAULT_AUDIENCE = "platform_api";
12
- /**
13
- * Default OAuth scopes
14
- */
15
8
  export declare const DEFAULT_SCOPE = "openid profile email offline_access transact";
16
- /**
17
- * NextAuth credentials provider ID for Immutable
18
- */
19
9
  export declare const IMMUTABLE_PROVIDER_ID = "immutable";
20
- /**
21
- * Default NextAuth API base path
22
- */
23
10
  export declare const DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
24
- /**
25
- * Default token expiry in seconds (15 minutes)
26
- * Used as fallback when exp claim cannot be extracted from JWT
27
- */
28
- export declare const DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
29
- /**
30
- * Default token expiry in milliseconds
31
- */
32
- export declare const DEFAULT_TOKEN_EXPIRY_MS: number;
33
- /**
34
- * Buffer time in milliseconds before token expiry to trigger refresh.
35
- * Matches TOKEN_EXPIRY_BUFFER_SECONDS (60s) in @imtbl/auth-next-server.
36
- */
37
- export declare const TOKEN_EXPIRY_BUFFER_MS: number;
11
+ export declare const DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
12
+ export declare const DEFAULT_REDIRECT_URI_PATH = "/callback";
13
+ export declare const DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
14
+ export declare const DEFAULT_TOKEN_EXPIRY_MS = 900000;
15
+ export declare const TOKEN_EXPIRY_BUFFER_MS = 60000;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sandbox default redirect URI for zero-config mode.
3
+ * Defined locally to avoid importing from auth-next-server (which uses next/server).
4
+ * OAuth requires an absolute URL; this runs in the browser when login is invoked.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function deriveDefaultRedirectUri(): string;
@@ -86,53 +86,87 @@ export interface UseImmutableSessionReturn {
86
86
  export declare function useImmutableSession(): UseImmutableSessionReturn;
87
87
  /**
88
88
  * Return type for useLogin hook
89
+ *
90
+ * Config is optional - when omitted, defaults are auto-derived (clientId, redirectUri, etc.).
91
+ * When provided, must be a complete LoginConfig.
89
92
  */
90
93
  export interface UseLoginReturn {
91
94
  /** Start login with popup flow */
92
- loginWithPopup: (config: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
95
+ loginWithPopup: (config?: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
93
96
  /** Start login with embedded modal flow */
94
- loginWithEmbedded: (config: LoginConfig) => Promise<void>;
97
+ loginWithEmbedded: (config?: LoginConfig) => Promise<void>;
95
98
  /** Start login with redirect flow (navigates away from page) */
96
- loginWithRedirect: (config: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
99
+ loginWithRedirect: (config?: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
97
100
  /** Whether login is currently in progress */
98
101
  isLoggingIn: boolean;
99
102
  /** Error message from the last login attempt, or null if none */
100
103
  error: string | null;
101
104
  }
102
105
  /**
103
- * Hook to handle Immutable authentication login flows.
106
+ * Hook to handle Immutable authentication login flows with automatic defaults.
104
107
  *
105
108
  * Provides login functions that:
106
109
  * 1. Handle OAuth authentication via popup, embedded modal, or redirect
107
110
  * 2. Automatically sign in to NextAuth after successful authentication
108
111
  * 3. Track loading and error states
112
+ * 4. Auto-detect clientId and redirectUri if not provided (uses defaults)
109
113
  *
110
- * Config is passed at call time to allow different configurations for different
111
- * login methods (e.g., different redirectUri vs popupRedirectUri).
114
+ * Config can be passed at call time or omitted to use sensible defaults:
115
+ * - `clientId`: Auto-detected based on environment (sandbox vs production)
116
+ * - `redirectUri`: Auto-derived from `window.location.origin + '/callback'`
117
+ * - `popupRedirectUri`: Auto-derived from `window.location.origin + '/callback'` (same as redirectUri)
118
+ * - `logoutRedirectUri`: Auto-derived from `window.location.origin`
119
+ * - `scope`: `'openid profile email offline_access transact'`
120
+ * - `audience`: `'platform_api'`
121
+ * - `authenticationDomain`: `'https://auth.immutable.com'`
112
122
  *
113
123
  * Must be used within a SessionProvider from next-auth/react.
114
124
  *
115
- * @example
125
+ * @example Minimal usage (uses all defaults)
116
126
  * ```tsx
117
127
  * import { useLogin, useImmutableSession } from '@imtbl/auth-next-client';
118
128
  *
119
- * const config = {
120
- * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
121
- * redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
122
- * popupRedirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback/popup`,
123
- * };
129
+ * function LoginButton() {
130
+ * const { isAuthenticated } = useImmutableSession();
131
+ * const { loginWithPopup, isLoggingIn, error } = useLogin();
132
+ *
133
+ * if (isAuthenticated) {
134
+ * return <p>You are logged in!</p>;
135
+ * }
136
+ *
137
+ * return (
138
+ * <>
139
+ * <button onClick={() => loginWithPopup()} disabled={isLoggingIn}>
140
+ * {isLoggingIn ? 'Signing in...' : 'Sign In'}
141
+ * </button>
142
+ * {error && <p style={{ color: 'red' }}>{error}</p>}
143
+ * </>
144
+ * );
145
+ * }
146
+ * ```
147
+ *
148
+ * @example With custom configuration
149
+ * ```tsx
150
+ * import { useLogin, useImmutableSession } from '@imtbl/auth-next-client';
124
151
  *
125
152
  * function LoginButton() {
126
153
  * const { isAuthenticated } = useImmutableSession();
127
154
  * const { loginWithPopup, isLoggingIn, error } = useLogin();
128
155
  *
156
+ * const handleLogin = () => {
157
+ * loginWithPopup({
158
+ * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID,
159
+ * redirectUri: `${window.location.origin}/callback`,
160
+ * });
161
+ * };
162
+ *
129
163
  * if (isAuthenticated) {
130
164
  * return <p>You are logged in!</p>;
131
165
  * }
132
166
  *
133
167
  * return (
134
168
  * <>
135
- * <button onClick={() => loginWithPopup(config)} disabled={isLoggingIn}>
169
+ * <button onClick={handleLogin} disabled={isLoggingIn}>
136
170
  * {isLoggingIn ? 'Signing in...' : 'Sign In'}
137
171
  * </button>
138
172
  * {error && <p style={{ color: 'red' }}>{error}</p>}
@@ -152,9 +186,11 @@ export interface UseLogoutReturn {
152
186
  * This ensures that when the user logs in again, they will be prompted to select
153
187
  * an account instead of being automatically logged in with the previous account.
154
188
  *
155
- * @param config - Logout configuration with clientId and optional redirectUri
189
+ * Config is optional - defaults will be auto-derived if not provided.
190
+ *
191
+ * @param config - Optional logout configuration with clientId and optional redirectUri
156
192
  */
157
- logout: (config: LogoutConfig) => Promise<void>;
193
+ logout: (config?: LogoutConfig) => Promise<void>;
158
194
  /** Whether logout is currently in progress */
159
195
  isLoggingOut: boolean;
160
196
  /** Error message from the last logout attempt, or null if none */
@@ -171,16 +207,38 @@ export interface UseLogoutReturn {
171
207
  * an account (for social logins like Google) instead of being automatically logged
172
208
  * in with the previous account.
173
209
  *
210
+ * Config is optional - defaults will be auto-derived if not provided:
211
+ * - `clientId`: Auto-detected based on environment (sandbox vs production)
212
+ * - `logoutRedirectUri`: Auto-derived from `window.location.origin`
213
+ *
174
214
  * Must be used within a SessionProvider from next-auth/react.
175
215
  *
176
- * @example
216
+ * @example Minimal usage (uses all defaults)
177
217
  * ```tsx
178
218
  * import { useLogout, useImmutableSession } from '@imtbl/auth-next-client';
179
219
  *
180
- * const logoutConfig = {
181
- * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
182
- * logoutRedirectUri: process.env.NEXT_PUBLIC_BASE_URL!,
183
- * };
220
+ * function LogoutButton() {
221
+ * const { isAuthenticated } = useImmutableSession();
222
+ * const { logout, isLoggingOut, error } = useLogout();
223
+ *
224
+ * if (!isAuthenticated) {
225
+ * return null;
226
+ * }
227
+ *
228
+ * return (
229
+ * <>
230
+ * <button onClick={() => logout()} disabled={isLoggingOut}>
231
+ * {isLoggingOut ? 'Signing out...' : 'Sign Out'}
232
+ * </button>
233
+ * {error && <p style={{ color: 'red' }}>{error}</p>}
234
+ * </>
235
+ * );
236
+ * }
237
+ * ```
238
+ *
239
+ * @example With custom configuration
240
+ * ```tsx
241
+ * import { useLogout, useImmutableSession } from '@imtbl/auth-next-client';
184
242
  *
185
243
  * function LogoutButton() {
186
244
  * const { isAuthenticated } = useImmutableSession();
@@ -192,7 +250,13 @@ export interface UseLogoutReturn {
192
250
  *
193
251
  * return (
194
252
  * <>
195
- * <button onClick={() => logout(logoutConfig)} disabled={isLoggingOut}>
253
+ * <button
254
+ * onClick={() => logout({
255
+ * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID,
256
+ * logoutRedirectUri: `${window.location.origin}/custom-logout`,
257
+ * })}
258
+ * disabled={isLoggingOut}
259
+ * >
196
260
  * {isLoggingOut ? 'Signing out...' : 'Sign Out'}
197
261
  * </button>
198
262
  * {error && <p style={{ color: 'red' }}>{error}</p>}
@@ -28,3 +28,5 @@ export type { ImmutableUserClient, ImmutableTokenDataClient, ZkEvmInfo, } from '
28
28
  export type { ImmutableAuthConfig, ImmutableTokenData, ImmutableUser, AuthProps, AuthPropsWithData, ProtectedAuthProps, ProtectedAuthPropsWithData, } from '@imtbl/auth-next-server';
29
29
  export type { LoginConfig, StandaloneLoginOptions, DirectLoginOptions, LogoutConfig, } from '@imtbl/auth';
30
30
  export { MarketingConsentStatus } from '@imtbl/auth';
31
+ export { DEFAULT_AUTH_DOMAIN, DEFAULT_AUDIENCE, DEFAULT_SCOPE, IMMUTABLE_PROVIDER_ID, DEFAULT_SANDBOX_CLIENT_ID, DEFAULT_REDIRECT_URI_PATH, DEFAULT_LOGOUT_REDIRECT_URI_PATH, } from './constants';
32
+ export { deriveDefaultRedirectUri } from './defaultConfig';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imtbl/auth-next-client",
3
- "version": "2.12.7-alpha.8",
3
+ "version": "2.12.7",
4
4
  "description": "Immutable Auth.js v5 integration for Next.js - Client-side components",
5
5
  "author": "Immutable",
6
6
  "license": "Apache-2.0",
@@ -27,11 +27,11 @@
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
- "@imtbl/auth": "2.12.7-alpha.8",
31
- "@imtbl/auth-next-server": "2.12.7-alpha.8"
30
+ "@imtbl/auth": "2.12.7",
31
+ "@imtbl/auth-next-server": "2.12.7"
32
32
  },
33
33
  "peerDependencies": {
34
- "next": "^15.0.0",
34
+ "next": "^14.0.0 || ^15.0.0",
35
35
  "next-auth": "^5.0.0-beta.25",
36
36
  "react": "^18.2.0 || ^19.0.0"
37
37
  },
@@ -56,7 +56,7 @@
56
56
  "@types/react": "^18.3.5",
57
57
  "eslint": "^8.56.0",
58
58
  "jest": "^29.7.0",
59
- "next": "^15.1.6",
59
+ "next": "^15.2.6",
60
60
  "next-auth": "^5.0.0-beta.30",
61
61
  "react": "^18.2.0",
62
62
  "tsup": "^8.3.0",
package/src/constants.ts CHANGED
@@ -1,45 +1,16 @@
1
1
  /**
2
- * Shared constants for @imtbl/auth-next-client
2
+ * Client-side constants for @imtbl/auth-next-client.
3
+ * Defined locally to avoid importing from auth-next-server (which uses next/server).
4
+ * Values must stay in sync with auth-next-server constants.
3
5
  */
4
6
 
5
- /**
6
- * Default Immutable authentication domain
7
- */
8
7
  export const DEFAULT_AUTH_DOMAIN = 'https://auth.immutable.com';
9
-
10
- /**
11
- * Default OAuth audience
12
- */
13
8
  export const DEFAULT_AUDIENCE = 'platform_api';
14
-
15
- /**
16
- * Default OAuth scopes
17
- */
18
9
  export const DEFAULT_SCOPE = 'openid profile email offline_access transact';
19
-
20
- /**
21
- * NextAuth credentials provider ID for Immutable
22
- */
23
10
  export const IMMUTABLE_PROVIDER_ID = 'immutable';
24
-
25
- /**
26
- * Default NextAuth API base path
27
- */
28
11
  export const DEFAULT_NEXTAUTH_BASE_PATH = '/api/auth';
29
-
30
- /**
31
- * Default token expiry in seconds (15 minutes)
32
- * Used as fallback when exp claim cannot be extracted from JWT
33
- */
34
- export const DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
35
-
36
- /**
37
- * Default token expiry in milliseconds
38
- */
39
- export const DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1000;
40
-
41
- /**
42
- * Buffer time in milliseconds before token expiry to trigger refresh.
43
- * Matches TOKEN_EXPIRY_BUFFER_SECONDS (60s) in @imtbl/auth-next-server.
44
- */
45
- export const TOKEN_EXPIRY_BUFFER_MS = 60 * 1000;
12
+ export const DEFAULT_SANDBOX_CLIENT_ID = 'mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo';
13
+ export const DEFAULT_REDIRECT_URI_PATH = '/callback';
14
+ export const DEFAULT_LOGOUT_REDIRECT_URI_PATH = '/';
15
+ export const DEFAULT_TOKEN_EXPIRY_MS = 900_000;
16
+ export const TOKEN_EXPIRY_BUFFER_MS = 60_000;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Sandbox default redirect URI for zero-config mode.
3
+ * Defined locally to avoid importing from auth-next-server (which uses next/server).
4
+ * OAuth requires an absolute URL; this runs in the browser when login is invoked.
5
+ *
6
+ * @internal
7
+ */
8
+
9
+ import { DEFAULT_REDIRECT_URI_PATH } from './constants';
10
+
11
+ export function deriveDefaultRedirectUri(): string {
12
+ if (typeof window === 'undefined') {
13
+ throw new Error(
14
+ '[auth-next-client] deriveDefaultRedirectUri requires window. '
15
+ + 'Login hooks run in the browser when the user triggers login.',
16
+ );
17
+ }
18
+ return `${window.location.origin}${DEFAULT_REDIRECT_URI_PATH}`;
19
+ }
@@ -23,6 +23,10 @@ jest.mock('@imtbl/auth', () => ({
23
23
  logoutWithRedirect: jest.fn(),
24
24
  }));
25
25
 
26
+ jest.mock('./defaultConfig', () => ({
27
+ deriveDefaultRedirectUri: jest.fn(() => 'http://localhost:3000/callback'),
28
+ }));
29
+
26
30
  import { useImmutableSession } from './hooks';
27
31
 
28
32
  // ---------------------------------------------------------------------------
package/src/hooks.tsx CHANGED
@@ -18,7 +18,16 @@ import {
18
18
  loginWithRedirect as rawLoginWithRedirect,
19
19
  logoutWithRedirect as rawLogoutWithRedirect,
20
20
  } from '@imtbl/auth';
21
- import { IMMUTABLE_PROVIDER_ID, TOKEN_EXPIRY_BUFFER_MS } from './constants';
21
+ import { deriveDefaultRedirectUri } from './defaultConfig';
22
+ import {
23
+ IMMUTABLE_PROVIDER_ID,
24
+ TOKEN_EXPIRY_BUFFER_MS,
25
+ DEFAULT_SANDBOX_CLIENT_ID,
26
+ DEFAULT_LOGOUT_REDIRECT_URI_PATH,
27
+ DEFAULT_AUTH_DOMAIN,
28
+ DEFAULT_SCOPE,
29
+ DEFAULT_AUDIENCE,
30
+ } from './constants';
22
31
  import { storeIdToken, getStoredIdToken, clearStoredIdToken } from './idTokenStorage';
23
32
 
24
33
  // ---------------------------------------------------------------------------
@@ -42,6 +51,37 @@ function deduplicatedUpdate(
42
51
  return pendingRefresh;
43
52
  }
44
53
 
54
+ // ---------------------------------------------------------------------------
55
+ // Sandbox defaults for zero-config (no config or full config - no merge)
56
+ // ---------------------------------------------------------------------------
57
+
58
+ function getSandboxLoginConfig(): LoginConfig {
59
+ const redirectUri = deriveDefaultRedirectUri();
60
+ return {
61
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
62
+ redirectUri,
63
+ popupRedirectUri: redirectUri,
64
+ scope: DEFAULT_SCOPE,
65
+ audience: DEFAULT_AUDIENCE,
66
+ authenticationDomain: DEFAULT_AUTH_DOMAIN,
67
+ };
68
+ }
69
+
70
+ function getSandboxLogoutConfig(): LogoutConfig {
71
+ if (typeof window === 'undefined') {
72
+ throw new Error(
73
+ '[auth-next-client] getSandboxLogoutConfig requires window. '
74
+ + 'Logout runs in the browser when the user triggers it.',
75
+ );
76
+ }
77
+ const logoutRedirectUri = window.location.origin + DEFAULT_LOGOUT_REDIRECT_URI_PATH;
78
+ return {
79
+ clientId: DEFAULT_SANDBOX_CLIENT_ID,
80
+ logoutRedirectUri,
81
+ authenticationDomain: DEFAULT_AUTH_DOMAIN,
82
+ };
83
+ }
84
+
45
85
  /**
46
86
  * Internal session type with full token data (not exported).
47
87
  * Used internally by the hook for token validation, refresh logic, and getUser/getAccessToken.
@@ -341,14 +381,17 @@ export function useImmutableSession(): UseImmutableSessionReturn {
341
381
 
342
382
  /**
343
383
  * Return type for useLogin hook
384
+ *
385
+ * Config is optional - when omitted, defaults are auto-derived (clientId, redirectUri, etc.).
386
+ * When provided, must be a complete LoginConfig.
344
387
  */
345
388
  export interface UseLoginReturn {
346
389
  /** Start login with popup flow */
347
- loginWithPopup: (config: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
390
+ loginWithPopup: (config?: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
348
391
  /** Start login with embedded modal flow */
349
- loginWithEmbedded: (config: LoginConfig) => Promise<void>;
392
+ loginWithEmbedded: (config?: LoginConfig) => Promise<void>;
350
393
  /** Start login with redirect flow (navigates away from page) */
351
- loginWithRedirect: (config: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
394
+ loginWithRedirect: (config?: LoginConfig, options?: StandaloneLoginOptions) => Promise<void>;
352
395
  /** Whether login is currently in progress */
353
396
  isLoggingIn: boolean;
354
397
  /** Error message from the last login attempt, or null if none */
@@ -356,39 +399,70 @@ export interface UseLoginReturn {
356
399
  }
357
400
 
358
401
  /**
359
- * Hook to handle Immutable authentication login flows.
402
+ * Hook to handle Immutable authentication login flows with automatic defaults.
360
403
  *
361
404
  * Provides login functions that:
362
405
  * 1. Handle OAuth authentication via popup, embedded modal, or redirect
363
406
  * 2. Automatically sign in to NextAuth after successful authentication
364
407
  * 3. Track loading and error states
408
+ * 4. Auto-detect clientId and redirectUri if not provided (uses defaults)
365
409
  *
366
- * Config is passed at call time to allow different configurations for different
367
- * login methods (e.g., different redirectUri vs popupRedirectUri).
410
+ * Config can be passed at call time or omitted to use sensible defaults:
411
+ * - `clientId`: Auto-detected based on environment (sandbox vs production)
412
+ * - `redirectUri`: Auto-derived from `window.location.origin + '/callback'`
413
+ * - `popupRedirectUri`: Auto-derived from `window.location.origin + '/callback'` (same as redirectUri)
414
+ * - `logoutRedirectUri`: Auto-derived from `window.location.origin`
415
+ * - `scope`: `'openid profile email offline_access transact'`
416
+ * - `audience`: `'platform_api'`
417
+ * - `authenticationDomain`: `'https://auth.immutable.com'`
368
418
  *
369
419
  * Must be used within a SessionProvider from next-auth/react.
370
420
  *
371
- * @example
421
+ * @example Minimal usage (uses all defaults)
372
422
  * ```tsx
373
423
  * import { useLogin, useImmutableSession } from '@imtbl/auth-next-client';
374
424
  *
375
- * const config = {
376
- * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
377
- * redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
378
- * popupRedirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback/popup`,
379
- * };
425
+ * function LoginButton() {
426
+ * const { isAuthenticated } = useImmutableSession();
427
+ * const { loginWithPopup, isLoggingIn, error } = useLogin();
428
+ *
429
+ * if (isAuthenticated) {
430
+ * return <p>You are logged in!</p>;
431
+ * }
432
+ *
433
+ * return (
434
+ * <>
435
+ * <button onClick={() => loginWithPopup()} disabled={isLoggingIn}>
436
+ * {isLoggingIn ? 'Signing in...' : 'Sign In'}
437
+ * </button>
438
+ * {error && <p style={{ color: 'red' }}>{error}</p>}
439
+ * </>
440
+ * );
441
+ * }
442
+ * ```
443
+ *
444
+ * @example With custom configuration
445
+ * ```tsx
446
+ * import { useLogin, useImmutableSession } from '@imtbl/auth-next-client';
380
447
  *
381
448
  * function LoginButton() {
382
449
  * const { isAuthenticated } = useImmutableSession();
383
450
  * const { loginWithPopup, isLoggingIn, error } = useLogin();
384
451
  *
452
+ * const handleLogin = () => {
453
+ * loginWithPopup({
454
+ * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID,
455
+ * redirectUri: `${window.location.origin}/callback`,
456
+ * });
457
+ * };
458
+ *
385
459
  * if (isAuthenticated) {
386
460
  * return <p>You are logged in!</p>;
387
461
  * }
388
462
  *
389
463
  * return (
390
464
  * <>
391
- * <button onClick={() => loginWithPopup(config)} disabled={isLoggingIn}>
465
+ * <button onClick={handleLogin} disabled={isLoggingIn}>
392
466
  * {isLoggingIn ? 'Signing in...' : 'Sign In'}
393
467
  * </button>
394
468
  * {error && <p style={{ color: 'red' }}>{error}</p>}
@@ -434,16 +508,18 @@ export function useLogin(): UseLoginReturn {
434
508
  /**
435
509
  * Login with a popup window.
436
510
  * Opens a popup for OAuth authentication, then signs in to NextAuth.
511
+ * Config is optional - defaults will be auto-derived if not provided.
437
512
  */
438
513
  const loginWithPopup = useCallback(async (
439
- config: LoginConfig,
514
+ config?: LoginConfig,
440
515
  options?: StandaloneLoginOptions,
441
516
  ): Promise<void> => {
442
517
  setIsLoggingIn(true);
443
518
  setError(null);
444
519
 
445
520
  try {
446
- const tokens = await rawLoginWithPopup(config, options);
521
+ const fullConfig = config ?? getSandboxLoginConfig();
522
+ const tokens = await rawLoginWithPopup(fullConfig, options);
447
523
  await signInWithTokens(tokens);
448
524
  } catch (err) {
449
525
  const errorMessage = err instanceof Error ? err.message : 'Login failed';
@@ -457,13 +533,15 @@ export function useLogin(): UseLoginReturn {
457
533
  /**
458
534
  * Login with an embedded modal.
459
535
  * Shows a modal for login method selection, then opens a popup for OAuth.
536
+ * Config is optional - defaults will be auto-derived if not provided.
460
537
  */
461
- const loginWithEmbedded = useCallback(async (config: LoginConfig): Promise<void> => {
538
+ const loginWithEmbedded = useCallback(async (config?: LoginConfig): Promise<void> => {
462
539
  setIsLoggingIn(true);
463
540
  setError(null);
464
541
 
465
542
  try {
466
- const tokens = await rawLoginWithEmbedded(config);
543
+ const fullConfig = config ?? getSandboxLoginConfig();
544
+ const tokens = await rawLoginWithEmbedded(fullConfig);
467
545
  await signInWithTokens(tokens);
468
546
  } catch (err) {
469
547
  const errorMessage = err instanceof Error ? err.message : 'Login failed';
@@ -479,16 +557,18 @@ export function useLogin(): UseLoginReturn {
479
557
  * Redirects the page to OAuth authentication.
480
558
  * After authentication, the user will be redirected to your callback page.
481
559
  * Use the CallbackPage component to complete the flow.
560
+ * Config is optional - defaults will be auto-derived if not provided.
482
561
  */
483
562
  const loginWithRedirect = useCallback(async (
484
- config: LoginConfig,
563
+ config?: LoginConfig,
485
564
  options?: StandaloneLoginOptions,
486
565
  ): Promise<void> => {
487
566
  setIsLoggingIn(true);
488
567
  setError(null);
489
568
 
490
569
  try {
491
- await rawLoginWithRedirect(config, options);
570
+ const fullConfig = config ?? getSandboxLoginConfig();
571
+ await rawLoginWithRedirect(fullConfig, options);
492
572
  // Note: The page will redirect, so this code may not run
493
573
  } catch (err) {
494
574
  const errorMessage = err instanceof Error ? err.message : 'Login failed';
@@ -518,9 +598,11 @@ export interface UseLogoutReturn {
518
598
  * This ensures that when the user logs in again, they will be prompted to select
519
599
  * an account instead of being automatically logged in with the previous account.
520
600
  *
521
- * @param config - Logout configuration with clientId and optional redirectUri
601
+ * Config is optional - defaults will be auto-derived if not provided.
602
+ *
603
+ * @param config - Optional logout configuration with clientId and optional redirectUri
522
604
  */
523
- logout: (config: LogoutConfig) => Promise<void>;
605
+ logout: (config?: LogoutConfig) => Promise<void>;
524
606
  /** Whether logout is currently in progress */
525
607
  isLoggingOut: boolean;
526
608
  /** Error message from the last logout attempt, or null if none */
@@ -538,16 +620,38 @@ export interface UseLogoutReturn {
538
620
  * an account (for social logins like Google) instead of being automatically logged
539
621
  * in with the previous account.
540
622
  *
623
+ * Config is optional - defaults will be auto-derived if not provided:
624
+ * - `clientId`: Auto-detected based on environment (sandbox vs production)
625
+ * - `logoutRedirectUri`: Auto-derived from `window.location.origin`
626
+ *
541
627
  * Must be used within a SessionProvider from next-auth/react.
542
628
  *
543
- * @example
629
+ * @example Minimal usage (uses all defaults)
544
630
  * ```tsx
545
631
  * import { useLogout, useImmutableSession } from '@imtbl/auth-next-client';
546
632
  *
547
- * const logoutConfig = {
548
- * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
549
- * logoutRedirectUri: process.env.NEXT_PUBLIC_BASE_URL!,
550
- * };
633
+ * function LogoutButton() {
634
+ * const { isAuthenticated } = useImmutableSession();
635
+ * const { logout, isLoggingOut, error } = useLogout();
636
+ *
637
+ * if (!isAuthenticated) {
638
+ * return null;
639
+ * }
640
+ *
641
+ * return (
642
+ * <>
643
+ * <button onClick={() => logout()} disabled={isLoggingOut}>
644
+ * {isLoggingOut ? 'Signing out...' : 'Sign Out'}
645
+ * </button>
646
+ * {error && <p style={{ color: 'red' }}>{error}</p>}
647
+ * </>
648
+ * );
649
+ * }
650
+ * ```
651
+ *
652
+ * @example With custom configuration
653
+ * ```tsx
654
+ * import { useLogout, useImmutableSession } from '@imtbl/auth-next-client';
551
655
  *
552
656
  * function LogoutButton() {
553
657
  * const { isAuthenticated } = useImmutableSession();
@@ -559,7 +663,13 @@ export interface UseLogoutReturn {
559
663
  *
560
664
  * return (
561
665
  * <>
562
- * <button onClick={() => logout(logoutConfig)} disabled={isLoggingOut}>
666
+ * <button
667
+ * onClick={() => logout({
668
+ * clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID,
669
+ * logoutRedirectUri: `${window.location.origin}/custom-logout`,
670
+ * })}
671
+ * disabled={isLoggingOut}
672
+ * >
563
673
  * {isLoggingOut ? 'Signing out...' : 'Sign Out'}
564
674
  * </button>
565
675
  * {error && <p style={{ color: 'red' }}>{error}</p>}
@@ -575,8 +685,9 @@ export function useLogout(): UseLogoutReturn {
575
685
  /**
576
686
  * Logout with federated logout.
577
687
  * First clears the NextAuth session, then redirects to the auth domain's logout endpoint.
688
+ * Config is optional - defaults will be auto-derived if not provided.
578
689
  */
579
- const logout = useCallback(async (config: LogoutConfig): Promise<void> => {
690
+ const logout = useCallback(async (config?: LogoutConfig): Promise<void> => {
580
691
  setIsLoggingOut(true);
581
692
  setError(null);
582
693
 
@@ -588,10 +699,13 @@ export function useLogout(): UseLogoutReturn {
588
699
  // We use redirect: false to handle the redirect ourselves for federated logout
589
700
  await signOut({ redirect: false });
590
701
 
702
+ // Create full config with defaults
703
+ const fullConfig = config ?? getSandboxLogoutConfig();
704
+
591
705
  // Redirect to the auth domain's logout endpoint using the standalone function
592
706
  // This clears the upstream session (Auth0/Immutable) so that on next login,
593
707
  // the user will be prompted to select an account instead of auto-logging in
594
- rawLogoutWithRedirect(config);
708
+ rawLogoutWithRedirect(fullConfig);
595
709
  } catch (err) {
596
710
  const errorMessage = err instanceof Error ? err.message : 'Logout failed';
597
711
  setError(errorMessage);
package/src/index.ts CHANGED
@@ -59,3 +59,15 @@ export type {
59
59
  LogoutConfig,
60
60
  } from '@imtbl/auth';
61
61
  export { MarketingConsentStatus } from '@imtbl/auth';
62
+
63
+ // Re-export constants and default config helpers for consumer convenience
64
+ export {
65
+ DEFAULT_AUTH_DOMAIN,
66
+ DEFAULT_AUDIENCE,
67
+ DEFAULT_SCOPE,
68
+ IMMUTABLE_PROVIDER_ID,
69
+ DEFAULT_SANDBOX_CLIENT_ID,
70
+ DEFAULT_REDIRECT_URI_PATH,
71
+ DEFAULT_LOGOUT_REDIRECT_URI_PATH,
72
+ } from './constants';
73
+ export { deriveDefaultRedirectUri } from './defaultConfig';