Skip to content

Commit 103c7e7

Browse files
feat(auth-next-client,auth-next-server): add default auth with auto-detection
Implement default authentication for auth-next-client/server packages to provide zero-config setup for developers. auth-next-server changes: - Add createDefaultAuthConfig() function with optional configuration - Auto-detect clientId based on environment (sandbox vs production) - Auto-derive redirectUri from window.location.origin + '/callback' - Export default client IDs and constants for consumer use - Uses public Immutable client IDs for development convenience auth-next-client changes: - Update useLogin hook to accept optional config (all fields) - Update useLogout hook to accept optional config - Add helper functions to create complete configs with defaults - Auto-detect clientId, redirectUri, popupRedirectUri, logoutRedirectUri - Export default constants for direct consumer use - Align popupRedirectUri behavior with @imtbl/auth and @imtbl/wallet (uses same '/callback' path instead of separate '/callback/popup') This enables minimal setup: // Server (lib/auth.ts) export const { handlers, auth } = NextAuth(createDefaultAuthConfig()); // Client const { loginWithPopup } = useLogin(); await loginWithPopup(); // No config needed! const { logout } = useLogout(); await logout(); // No config needed! Consumers can still override any field as needed for production use. All tests passed: ✅ Zero-config login with popup ✅ Zero-config logout with federated logout ✅ Custom config overrides (partial and full) ✅ Session management ✅ Auto-detection of environment (sandbox vs production) Related to wallet package default auth implementation (PR #2768). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ef313b9 commit 103c7e7

5 files changed

Lines changed: 661 additions & 572 deletions

File tree

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,16 @@ export type {
5959
LogoutConfig,
6060
} from '@imtbl/auth';
6161
export { MarketingConsentStatus } from '@imtbl/auth';
62+
63+
// Re-export constants for consumer convenience
64+
export {
65+
DEFAULT_AUTH_DOMAIN,
66+
DEFAULT_AUDIENCE,
67+
DEFAULT_SCOPE,
68+
IMMUTABLE_PROVIDER_ID,
69+
DEFAULT_PRODUCTION_CLIENT_ID,
70+
DEFAULT_SANDBOX_CLIENT_ID,
71+
DEFAULT_REDIRECT_URI_PATH,
72+
DEFAULT_POPUP_REDIRECT_URI_PATH,
73+
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
74+
} from './constants';

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,61 @@ import {
99
DEFAULT_AUTH_DOMAIN,
1010
IMMUTABLE_PROVIDER_ID,
1111
DEFAULT_SESSION_MAX_AGE_SECONDS,
12+
DEFAULT_PRODUCTION_CLIENT_ID,
13+
DEFAULT_SANDBOX_CLIENT_ID,
14+
DEFAULT_REDIRECT_URI_PATH,
1215
} from './constants';
1316

1417
// Handle ESM/CJS interop - in some bundler configurations, the default export
1518
// may be nested under a 'default' property
1619
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1720
const Credentials = ((CredentialsImport as any).default || CredentialsImport) as typeof CredentialsImport;
1821

22+
/**
23+
* Detect if we're in a sandbox/test environment based on the current URL.
24+
* Checks if the hostname includes 'sandbox' or 'localhost'.
25+
* Server-side safe: returns false if window is not available.
26+
*
27+
* @returns true if in sandbox environment, false otherwise
28+
* @internal
29+
*/
30+
function isSandboxEnvironment(): boolean {
31+
if (typeof window === 'undefined') {
32+
// Server-side: cannot detect, default to production for safety
33+
return false;
34+
}
35+
36+
const hostname = window.location.hostname.toLowerCase();
37+
return hostname.includes('sandbox') || hostname.includes('localhost');
38+
}
39+
40+
/**
41+
* Derive the default clientId based on the environment.
42+
* Uses public Immutable client IDs for sandbox and production.
43+
*
44+
* @returns Default client ID for the current environment
45+
* @internal
46+
*/
47+
function deriveDefaultClientId(): string {
48+
return isSandboxEnvironment() ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID;
49+
}
50+
51+
/**
52+
* Derive the default redirectUri based on the current URL.
53+
* Server-side safe: returns a placeholder that will be replaced client-side.
54+
*
55+
* @returns Default redirect URI
56+
* @internal
57+
*/
58+
function deriveDefaultRedirectUri(): string {
59+
if (typeof window === 'undefined') {
60+
// Server-side: return path only, will be combined with window.location.origin client-side
61+
return DEFAULT_REDIRECT_URI_PATH;
62+
}
63+
64+
return `${window.location.origin}${DEFAULT_REDIRECT_URI_PATH}`;
65+
}
66+
1967
/**
2068
* Validate tokens by calling the userinfo endpoint.
2169
* This is the standard OAuth 2.0 way to validate access tokens server-side.
@@ -304,3 +352,50 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
304352

305353
// Keep backwards compatibility alias
306354
export const createAuthOptions = createAuthConfig;
355+
356+
/**
357+
* Create Auth.js v5 configuration for Immutable authentication with all parameters optional.
358+
*
359+
* This is a convenience wrapper around `createAuthConfig` that provides sensible defaults:
360+
* - Auto-detects `clientId` based on environment (sandbox vs production)
361+
* - Auto-derives `redirectUri` from `window.location.origin + '/callback'`
362+
* - Uses default values for `audience`, `scope`, and `authenticationDomain`
363+
*
364+
* **Important**: This uses public Immutable client IDs for development convenience.
365+
* For production applications, you should use your own client ID from Immutable Hub.
366+
*
367+
* @param config - Optional partial configuration. All fields can be overridden.
368+
* @returns Auth.js v5 configuration object
369+
*
370+
* @example
371+
* ```typescript
372+
* // Minimal setup - all defaults
373+
* import NextAuth from "next-auth";
374+
* import { createDefaultAuthConfig } from "@imtbl/auth-next-server";
375+
*
376+
* export const { handlers, auth, signIn, signOut } = NextAuth(createDefaultAuthConfig());
377+
* ```
378+
*
379+
* @example
380+
* ```typescript
381+
* // Override specific fields
382+
* import NextAuth from "next-auth";
383+
* import { createDefaultAuthConfig } from "@imtbl/auth-next-server";
384+
*
385+
* export const { handlers, auth, signIn, signOut } = NextAuth(createDefaultAuthConfig({
386+
* clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID, // Use your own client ID
387+
* }));
388+
* ```
389+
*/
390+
export function createDefaultAuthConfig(config?: Partial<ImmutableAuthConfig>): NextAuthConfig {
391+
const clientId = config?.clientId || deriveDefaultClientId();
392+
const redirectUri = config?.redirectUri || deriveDefaultRedirectUri();
393+
394+
return createAuthConfig({
395+
clientId,
396+
redirectUri,
397+
audience: config?.audience,
398+
scope: config?.scope,
399+
authenticationDomain: config?.authenticationDomain,
400+
});
401+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,15 @@ export const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
4949
* This is how long the NextAuth session cookie will be valid
5050
*/
5151
export const DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
52+
53+
/**
54+
* Default Client IDs for auto-detection
55+
* These are public client IDs for Immutable's default applications
56+
*/
57+
export const DEFAULT_PRODUCTION_CLIENT_ID = 'PtQRK4iRJ8GkXjiz6xfImMAYhPhW0cYk';
58+
export const DEFAULT_SANDBOX_CLIENT_ID = 'mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo';
59+
60+
/**
61+
* Default redirect URI fallback (will be replaced at runtime with window.location.origin + '/callback')
62+
*/
63+
export const DEFAULT_REDIRECT_URI_PATH = '/callback';

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,24 @@ export { default as NextAuth } from 'next-auth';
3737
// Re-export config utilities
3838
// ============================================================================
3939

40-
export { createAuthConfig, createAuthOptions } from './config';
40+
export { createAuthConfig, createDefaultAuthConfig, createAuthOptions } from './config';
4141
export {
4242
isTokenExpired,
4343
refreshAccessToken,
4444
extractZkEvmFromIdToken,
4545
type RefreshedTokens,
4646
type ZkEvmData,
4747
} from './refresh';
48+
export {
49+
DEFAULT_AUTH_DOMAIN,
50+
DEFAULT_AUDIENCE,
51+
DEFAULT_SCOPE,
52+
IMMUTABLE_PROVIDER_ID,
53+
DEFAULT_NEXTAUTH_BASE_PATH,
54+
DEFAULT_PRODUCTION_CLIENT_ID,
55+
DEFAULT_SANDBOX_CLIENT_ID,
56+
DEFAULT_REDIRECT_URI_PATH,
57+
} from './constants';
4858

4959
// ============================================================================
5060
// Type exports

0 commit comments

Comments
 (0)