Skip to content

Commit 866ebd8

Browse files
refactor(auth-next): simplify config to no config or full config policy
- auth-next-server: remove redundant clientId/redirectUri validation (type already requires them); clean up defaultConfig and constants comments - auth-next-client: replace createDefault* merge helpers with getSandbox*Config; use config ?? sandboxDefaults at call sites Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6366c33 commit 866ebd8

11 files changed

Lines changed: 78 additions & 137 deletions

File tree

examples/passport/wallets-connect-with-nextjs/app/callback-auth-next/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"use client";
22

3-
import { CallbackPage, deriveDefaultClientId } from "@imtbl/auth-next-client";
3+
import { CallbackPage, DEFAULT_SANDBOX_CLIENT_ID } from "@imtbl/auth-next-client";
44

55
export default function AuthNextCallback() {
6-
// Use deriveDefaultClientId() to match login flow (zero config = sandbox)
6+
// Use DEFAULT_SANDBOX_CLIENT_ID to match login flow (zero config = sandbox)
77
const config = {
8-
clientId: deriveDefaultClientId(),
8+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
99
redirectUri: typeof window !== 'undefined' ? `${window.location.origin}/callback-auth-next` : '/callback-auth-next',
1010
};
1111

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
/**
2-
* Re-export constants from auth-next-server for consistency.
3-
* All shared constants live in auth-next-server to avoid duplication.
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.
45
*/
5-
export {
6-
DEFAULT_AUTH_DOMAIN,
7-
DEFAULT_AUDIENCE,
8-
DEFAULT_SCOPE,
9-
IMMUTABLE_PROVIDER_ID,
10-
DEFAULT_NEXTAUTH_BASE_PATH,
11-
DEFAULT_PRODUCTION_CLIENT_ID,
12-
DEFAULT_SANDBOX_CLIENT_ID,
13-
DEFAULT_REDIRECT_URI_PATH,
14-
DEFAULT_POPUP_REDIRECT_URI_PATH,
15-
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
16-
DEFAULT_TOKEN_EXPIRY_MS,
17-
TOKEN_EXPIRY_BUFFER_MS,
18-
} from '@imtbl/auth-next-server';
6+
7+
export const DEFAULT_AUTH_DOMAIN = 'https://auth.immutable.com';
8+
export const DEFAULT_AUDIENCE = 'platform_api';
9+
export const DEFAULT_SCOPE = 'openid profile email offline_access transact';
10+
export const IMMUTABLE_PROVIDER_ID = 'immutable';
11+
export const DEFAULT_NEXTAUTH_BASE_PATH = '/api/auth';
12+
export const DEFAULT_PRODUCTION_CLIENT_ID = 'PtQRK4iRJ8GkXjiz6xfImMAYhPhW0cYk';
13+
export const DEFAULT_SANDBOX_CLIENT_ID = 'mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo';
14+
export const DEFAULT_REDIRECT_URI_PATH = '/callback';
15+
export const DEFAULT_POPUP_REDIRECT_URI_PATH = '/callback';
16+
export const DEFAULT_LOGOUT_REDIRECT_URI_PATH = '/';
17+
export const DEFAULT_TOKEN_EXPIRY_MS = 900_000;
18+
export const TOKEN_EXPIRY_BUFFER_MS = 60_000;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
* Server: path only. Client: full URL (origin + path).
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+
return DEFAULT_REDIRECT_URI_PATH;
14+
}
15+
return `${window.location.origin}${DEFAULT_REDIRECT_URI_PATH}`;
16+
}

packages/auth-next-client/src/hooks.test.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,7 @@ jest.mock('@imtbl/auth', () => ({
2323
logoutWithRedirect: jest.fn(),
2424
}));
2525

26-
// Mock auth-next-server to avoid loading next/server (Request not defined in Node/Jest)
27-
jest.mock('@imtbl/auth-next-server', () => ({
28-
DEFAULT_AUTH_DOMAIN: 'https://auth.immutable.com',
29-
DEFAULT_AUDIENCE: 'platform_api',
30-
DEFAULT_SCOPE: 'openid profile email offline_access transact',
31-
IMMUTABLE_PROVIDER_ID: 'immutable',
32-
DEFAULT_NEXTAUTH_BASE_PATH: '/api/auth',
33-
DEFAULT_PRODUCTION_CLIENT_ID: 'prod-client-id',
34-
DEFAULT_SANDBOX_CLIENT_ID: 'sandbox-client-id',
35-
DEFAULT_REDIRECT_URI_PATH: '/callback',
36-
DEFAULT_POPUP_REDIRECT_URI_PATH: '/callback',
37-
DEFAULT_LOGOUT_REDIRECT_URI_PATH: '/',
38-
DEFAULT_TOKEN_EXPIRY_MS: 900000,
39-
TOKEN_EXPIRY_BUFFER_MS: 60000,
40-
deriveDefaultClientId: jest.fn(() => 'sandbox-client-id'),
26+
jest.mock('./defaultConfig', () => ({
4127
deriveDefaultRedirectUri: jest.fn(() => 'http://localhost:3000/callback'),
4228
}));
4329

packages/auth-next-client/src/hooks.tsx

Lines changed: 22 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ import {
1818
loginWithRedirect as rawLoginWithRedirect,
1919
logoutWithRedirect as rawLogoutWithRedirect,
2020
} from '@imtbl/auth';
21-
import {
22-
deriveDefaultClientId,
23-
deriveDefaultRedirectUri,
24-
} from '@imtbl/auth-next-server';
21+
import { deriveDefaultRedirectUri } from './defaultConfig';
2522
import {
2623
IMMUTABLE_PROVIDER_ID,
2724
TOKEN_EXPIRY_BUFFER_MS,
28-
DEFAULT_POPUP_REDIRECT_URI_PATH,
25+
DEFAULT_SANDBOX_CLIENT_ID,
2926
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
3027
DEFAULT_AUTH_DOMAIN,
3128
DEFAULT_SCOPE,
@@ -55,69 +52,29 @@ function deduplicatedUpdate(
5552
}
5653

5754
// ---------------------------------------------------------------------------
58-
// Default configuration helpers (extend shared logic from auth-next-server)
55+
// Sandbox defaults for zero-config (no config or full config - no merge)
5956
// ---------------------------------------------------------------------------
6057

61-
/**
62-
* Derive the default popupRedirectUri based on the current URL.
63-
*
64-
* @returns Default popup redirect URI
65-
* @internal
66-
*/
67-
function deriveDefaultPopupRedirectUri(): string {
68-
if (typeof window === 'undefined') {
69-
return DEFAULT_POPUP_REDIRECT_URI_PATH;
70-
}
71-
72-
return `${window.location.origin}${DEFAULT_POPUP_REDIRECT_URI_PATH}`;
73-
}
74-
75-
/**
76-
* Derive the default logoutRedirectUri based on the current URL.
77-
*
78-
* @returns Default logout redirect URI
79-
* @internal
80-
*/
81-
function deriveDefaultLogoutRedirectUri(): string {
82-
if (typeof window === 'undefined') {
83-
return DEFAULT_LOGOUT_REDIRECT_URI_PATH;
84-
}
85-
86-
return window.location.origin + DEFAULT_LOGOUT_REDIRECT_URI_PATH;
87-
}
88-
89-
/**
90-
* Create a complete LoginConfig with default values.
91-
* All fields are optional and will be auto-derived if not provided.
92-
*
93-
* @param config - Optional login configuration (when provided, must be complete)
94-
* @returns Complete LoginConfig with defaults applied
95-
* @internal
96-
*/
97-
function createDefaultLoginConfig(config?: LoginConfig): LoginConfig {
58+
function getSandboxLoginConfig(): LoginConfig {
59+
const redirectUri = deriveDefaultRedirectUri();
9860
return {
99-
clientId: config?.clientId || deriveDefaultClientId(),
100-
redirectUri: config?.redirectUri || deriveDefaultRedirectUri(),
101-
popupRedirectUri: config?.popupRedirectUri || deriveDefaultPopupRedirectUri(),
102-
scope: config?.scope || DEFAULT_SCOPE,
103-
audience: config?.audience || DEFAULT_AUDIENCE,
104-
authenticationDomain: config?.authenticationDomain || DEFAULT_AUTH_DOMAIN,
61+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
62+
redirectUri,
63+
popupRedirectUri: redirectUri,
64+
scope: DEFAULT_SCOPE,
65+
audience: DEFAULT_AUDIENCE,
66+
authenticationDomain: DEFAULT_AUTH_DOMAIN,
10567
};
10668
}
10769

108-
/**
109-
* Create a complete LogoutConfig with default values.
110-
* All fields are optional and will be auto-derived if not provided.
111-
*
112-
* @param config - Optional logout configuration (when provided, must be complete)
113-
* @returns Complete LogoutConfig with defaults applied
114-
* @internal
115-
*/
116-
function createDefaultLogoutConfig(config?: LogoutConfig): LogoutConfig {
70+
function getSandboxLogoutConfig(): LogoutConfig {
71+
const logoutRedirectUri = typeof window === 'undefined'
72+
? DEFAULT_LOGOUT_REDIRECT_URI_PATH
73+
: window.location.origin + DEFAULT_LOGOUT_REDIRECT_URI_PATH;
11774
return {
118-
clientId: config?.clientId || deriveDefaultClientId(),
119-
logoutRedirectUri: config?.logoutRedirectUri || deriveDefaultLogoutRedirectUri(),
120-
authenticationDomain: config?.authenticationDomain || DEFAULT_AUTH_DOMAIN,
75+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
76+
logoutRedirectUri,
77+
authenticationDomain: DEFAULT_AUTH_DOMAIN,
12178
};
12279
}
12380

@@ -557,7 +514,7 @@ export function useLogin(): UseLoginReturn {
557514
setError(null);
558515

559516
try {
560-
const fullConfig = createDefaultLoginConfig(config);
517+
const fullConfig = config ?? getSandboxLoginConfig();
561518
const tokens = await rawLoginWithPopup(fullConfig, options);
562519
await signInWithTokens(tokens);
563520
} catch (err) {
@@ -579,7 +536,7 @@ export function useLogin(): UseLoginReturn {
579536
setError(null);
580537

581538
try {
582-
const fullConfig = createDefaultLoginConfig(config);
539+
const fullConfig = config ?? getSandboxLoginConfig();
583540
const tokens = await rawLoginWithEmbedded(fullConfig);
584541
await signInWithTokens(tokens);
585542
} catch (err) {
@@ -606,7 +563,7 @@ export function useLogin(): UseLoginReturn {
606563
setError(null);
607564

608565
try {
609-
const fullConfig = createDefaultLoginConfig(config);
566+
const fullConfig = config ?? getSandboxLoginConfig();
610567
await rawLoginWithRedirect(fullConfig, options);
611568
// Note: The page will redirect, so this code may not run
612569
} catch (err) {
@@ -739,7 +696,7 @@ export function useLogout(): UseLogoutReturn {
739696
await signOut({ redirect: false });
740697

741698
// Create full config with defaults
742-
const fullConfig = createDefaultLogoutConfig(config);
699+
const fullConfig = config ?? getSandboxLogoutConfig();
743700

744701
// Redirect to the auth domain's logout endpoint using the standalone function
745702
// This clears the upstream session (Auth0/Immutable) so that on next login,

packages/auth-next-client/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ export {
7272
DEFAULT_POPUP_REDIRECT_URI_PATH,
7373
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
7474
} from './constants';
75-
export { deriveDefaultClientId, deriveDefaultRedirectUri } from '@imtbl/auth-next-server';
75+
export { deriveDefaultRedirectUri } from './defaultConfig';

packages/auth-next-server/src/config.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import type { ImmutableAuthConfig, ImmutableTokenData, UserInfoResponse } from '
88
import { isTokenExpired, refreshAccessToken, extractZkEvmFromIdToken } from './refresh';
99
import {
1010
DEFAULT_AUTH_DOMAIN,
11+
DEFAULT_SANDBOX_CLIENT_ID,
1112
IMMUTABLE_PROVIDER_ID,
1213
DEFAULT_SESSION_MAX_AGE_SECONDS,
1314
} from './constants';
14-
import { deriveDefaultClientId, deriveDefaultRedirectUri } from './defaultConfig';
15+
import { deriveDefaultRedirectUri } from './defaultConfig';
1516

1617
// Handle ESM/CJS interop - in some bundler configurations, the default export
1718
// may be nested under a 'default' property
@@ -90,16 +91,10 @@ export function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig {
9091
let redirectUri: string;
9192

9293
if (config) {
93-
if (!config.clientId || !config.redirectUri) {
94-
throw new Error(
95-
'[auth-next-server] When providing config, clientId and redirectUri are required. '
96-
+ 'Provide full config to avoid conflicts.',
97-
);
98-
}
9994
clientId = config.clientId;
10095
redirectUri = config.redirectUri;
10196
} else {
102-
clientId = deriveDefaultClientId();
97+
clientId = DEFAULT_SANDBOX_CLIENT_ID;
10398
redirectUri = deriveDefaultRedirectUri();
10499
}
105100

@@ -256,7 +251,7 @@ export function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig {
256251
error: undefined,
257252
};
258253
} catch (error) {
259-
// eslint-disable-next-line no-console
254+
// eslint-disable-next-line no-console
260255
console.error('[auth-next-server] Force refresh failed:', error);
261256
return {
262257
...token,
@@ -304,7 +299,7 @@ export function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig {
304299
error: undefined, // Clear any previous error
305300
};
306301
} catch (error) {
307-
// eslint-disable-next-line no-console
302+
// eslint-disable-next-line no-console
308303
console.error('[auth-next-server] Token refresh failed:', error);
309304
return {
310305
...token,

packages/auth-next-server/src/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ export const TOKEN_EXPIRY_BUFFER_MS = TOKEN_EXPIRY_BUFFER_SECONDS * 1000;
5757
export const DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
5858

5959
/**
60-
* Default Client IDs for auto-detection
61-
* These are public client IDs for Immutable's default applications
60+
* Public client IDs for Immutable's default applications.
61+
* auth-next zero-config uses DEFAULT_SANDBOX_CLIENT_ID only.
6262
*/
6363
export const DEFAULT_PRODUCTION_CLIENT_ID = 'PtQRK4iRJ8GkXjiz6xfImMAYhPhW0cYk';
6464
export const DEFAULT_SANDBOX_CLIENT_ID = 'mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo';
6565

6666
/**
67-
* Default redirect URI fallback (will be replaced at runtime with window.location.origin + '/callback')
67+
* Default redirect URI path for sandbox zero-config.
6868
*/
6969
export const DEFAULT_REDIRECT_URI_PATH = '/callback';
7070

packages/auth-next-server/src/defaultConfig.ts

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,18 @@
11
/**
2-
* Shared default configuration helpers for auth-next packages.
3-
* Used by both auth-next-server (config) and auth-next-client (hooks).
2+
* Sandbox default configuration for zero-config mode.
3+
* When createAuthConfig() is called with no args, these values are used.
4+
* Used by auth-next-server (config) and auth-next-client (hooks).
45
*
56
* @internal
67
*/
78

8-
import {
9-
DEFAULT_SANDBOX_CLIENT_ID,
10-
DEFAULT_REDIRECT_URI_PATH,
11-
} from './constants';
9+
import { DEFAULT_REDIRECT_URI_PATH } from './constants';
1210

1311
/**
14-
* Default client ID for zero-config mode.
15-
* When no config is provided, we always use sandbox to avoid conflicts.
16-
* Policy: provide nothing → full sandbox; provide config → provide everything.
12+
* Sandbox default redirect URI for zero-config mode.
13+
* Server: path only. Client: full URL (origin + path).
1714
*
18-
* @returns Sandbox client ID (used when createAuthConfig is called with no args)
19-
*/
20-
export function deriveDefaultClientId(): string {
21-
return DEFAULT_SANDBOX_CLIENT_ID;
22-
}
23-
24-
/**
25-
* Derive the default redirectUri based on the current URL.
26-
* Server-side safe: returns path only when window is undefined.
27-
*
28-
* @returns Default redirect URI
15+
* @returns Redirect URI path or full URL
2916
*/
3017
export function deriveDefaultRedirectUri(): string {
3118
if (typeof window === 'undefined') {

packages/auth-next-server/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export {
5959
DEFAULT_TOKEN_EXPIRY_MS,
6060
TOKEN_EXPIRY_BUFFER_MS,
6161
} from './constants';
62-
export { deriveDefaultClientId, deriveDefaultRedirectUri } from './defaultConfig';
62+
export { deriveDefaultRedirectUri } from './defaultConfig';
6363

6464
// ============================================================================
6565
// Type exports

0 commit comments

Comments
 (0)