Skip to content

Commit 6366c33

Browse files
refactor(auth-next-server): enforce zero-config=sandbox, full-config=provide-all
Align with team policy: provide nothing → full sandbox; provide config → provide everything. Zero config (createAuthConfig with no args): - Always use sandbox client ID (deriveDefaultClientId returns DEFAULT_SANDBOX_CLIENT_ID) - No environment detection; avoids conflicts when nothing is configured Full config (createAuthConfig with config): - Require clientId and redirectUri when config is provided - Throw if either is missing to prevent partial config conflicts - No merging with derived defaults when config is passed Bug fixes (Cursor Bugbot): - createDefaultLogoutConfig: forward authenticationDomain to avoid logout failures in custom envs - wallets-connect-with-nextjs callback: use deriveDefaultClientId() for consistency with login flow
1 parent badcef1 commit 6366c33

9 files changed

Lines changed: 48 additions & 54 deletions

File tree

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

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

3-
import { CallbackPage } from "@imtbl/auth-next-client";
4-
import {
5-
DEFAULT_PRODUCTION_CLIENT_ID,
6-
DEFAULT_SANDBOX_CLIENT_ID,
7-
} from "@imtbl/auth-next-client";
3+
import { CallbackPage, deriveDefaultClientId } from "@imtbl/auth-next-client";
84

95
export default function AuthNextCallback() {
10-
// Auto-detect environment and derive config
11-
const isSandbox = typeof window !== 'undefined' &&
12-
(window.location.hostname.includes('sandbox') || window.location.hostname.includes('localhost'));
13-
6+
// Use deriveDefaultClientId() to match login flow (zero config = sandbox)
147
const config = {
15-
clientId: isSandbox ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID,
8+
clientId: deriveDefaultClientId(),
169
redirectUri: typeof window !== 'undefined' ? `${window.location.origin}/callback-auth-next` : '/callback-auth-next',
1710
};
1811

packages/auth-next-client/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export default function Callback() {
106106

107107
### Default Auth (Zero Config)
108108

109-
When using `createAuthConfig()` with no args on the server, you can call login/logout with no config—clientId and redirectUri are auto-detected:
109+
When using `createAuthConfig()` with no args on the server, you can call login/logout with no config—sandbox clientId and redirectUri are used:
110110

111111
```tsx
112112
// With default auth - no config needed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ function createDefaultLogoutConfig(config?: LogoutConfig): LogoutConfig {
117117
return {
118118
clientId: config?.clientId || deriveDefaultClientId(),
119119
logoutRedirectUri: config?.logoutRedirectUri || deriveDefaultLogoutRedirectUri(),
120+
authenticationDomain: config?.authenticationDomain || DEFAULT_AUTH_DOMAIN,
120121
};
121122
}
122123

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export type {
6060
} from '@imtbl/auth';
6161
export { MarketingConsentStatus } from '@imtbl/auth';
6262

63-
// Re-export constants for consumer convenience
63+
// Re-export constants and default config helpers for consumer convenience
6464
export {
6565
DEFAULT_AUTH_DOMAIN,
6666
DEFAULT_AUDIENCE,
@@ -72,3 +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';

packages/auth-next-server/README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,13 @@ AUTH_SECRET=your-secret-key-min-32-characters
8181

8282
## Default Auth (Zero Config)
8383

84-
For development and quick prototyping, you can use `createAuthConfig()` with no configuration. It auto-detects:
85-
- `clientId` based on environment (sandbox for localhost/sandbox hostnames or when `NODE_ENV=development`, production otherwise)
86-
- `redirectUri` from `window.location.origin + '/callback'`
84+
Policy: **provide nothing → full sandbox; provide config → provide everything.**
8785

88-
> **Server-side detection:** On the server, `window` is unavailable, so sandbox is inferred from `NODE_ENV === 'development'`. This keeps client and server in sync when running locally with `next dev`. For production deployments, pass `clientId` explicitly.
86+
With no configuration, `createAuthConfig()` uses sandbox defaults:
87+
- `clientId`: sandbox (public Immutable client ID)
88+
- `redirectUri`: from `window.location.origin + '/callback'` (path only on server)
89+
90+
When providing config, pass `clientId` and `redirectUri` (and optionally `audience`, `scope`, `authenticationDomain`) to avoid conflicts.
8991

9092
```typescript
9193
// lib/auth.ts
@@ -99,10 +101,11 @@ export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig());
99101
With partial overrides:
100102

101103
```typescript
102-
// Override only clientId, rest uses defaults
104+
// With config - provide clientId and redirectUri
103105
export const { handlers, auth, signIn, signOut } = NextAuth(
104106
createAuthConfig({
105107
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
108+
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
106109
}),
107110
);
108111
```
@@ -140,8 +143,8 @@ const { handlers, auth, signIn, signOut } = NextAuth(
140143

141144
| Option | Type | Required | Description |
142145
| ---------------------- | -------- | -------- | ------------------------------------------------------------------------ |
143-
| `clientId` | `string` | Optional | Your Immutable application client ID (default: auto-detected) |
144-
| `redirectUri` | `string` | Yes | OAuth redirect URI configured in Immutable Hub |
146+
| `clientId` | `string` | Yes* | Your Immutable application client ID (*required when config is provided) |
147+
| `redirectUri` | `string` | Yes* | OAuth redirect URI (*required when config is provided) |
145148
| `audience` | `string` | No | OAuth audience (default: `"platform_api"`) |
146149
| `scope` | `string` | No | OAuth scopes (default: `"openid profile email offline_access transact"`) |
147150
| `authenticationDomain` | `string` | No | Auth domain (default: `"https://auth.immutable.com"`) |

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ async function validateTokens(
5858
/**
5959
* Create Auth.js v5 configuration for Immutable authentication.
6060
*
61-
* Config is optional - when omitted, sensible defaults are used:
62-
* - `clientId`: Auto-detected (sandbox for localhost/sandbox hostnames or NODE_ENV=development, production otherwise)
63-
* - `redirectUri`: Auto-derived from `window.location.origin + '/callback'` (path only on server)
61+
* Policy: provide nothing → full sandbox config; provide config → provide everything.
62+
* - Zero config: sandbox clientId, auto-derived redirectUri. No conflicts.
63+
* - With config: clientId and redirectUri required. Pass full config to avoid conflicts.
6464
*
65-
* @param config - Optional configuration. All fields can be overridden.
65+
* @param config - Optional. When omitted, uses sandbox defaults. When provided, clientId and redirectUri are required.
6666
*
6767
* @example
6868
* ```typescript
69-
* // Zero config - only AUTH_SECRET required in .env
69+
* // Zero config - sandbox, only AUTH_SECRET required in .env
7070
* import NextAuth from "next-auth";
7171
* import { createAuthConfig } from "@imtbl/auth-next-server";
7272
*
@@ -75,7 +75,7 @@ async function validateTokens(
7575
*
7676
* @example
7777
* ```typescript
78-
* // With custom config
78+
* // With config - provide clientId and redirectUri (and optionally audience, scope, authenticationDomain)
7979
* import NextAuth from "next-auth";
8080
* import { createAuthConfig } from "@imtbl/auth-next-server";
8181
*
@@ -85,9 +85,24 @@ async function validateTokens(
8585
* }));
8686
* ```
8787
*/
88-
export function createAuthConfig(config?: Partial<ImmutableAuthConfig>): NextAuthConfig {
89-
const clientId = config?.clientId || deriveDefaultClientId();
90-
const redirectUri = config?.redirectUri || deriveDefaultRedirectUri();
88+
export function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig {
89+
let clientId: string;
90+
let redirectUri: string;
91+
92+
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+
}
99+
clientId = config.clientId;
100+
redirectUri = config.redirectUri;
101+
} else {
102+
clientId = deriveDefaultClientId();
103+
redirectUri = deriveDefaultRedirectUri();
104+
}
105+
91106
const resolvedConfig: ImmutableAuthConfig = {
92107
clientId,
93108
redirectUri,

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

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,19 @@
66
*/
77

88
import {
9-
DEFAULT_PRODUCTION_CLIENT_ID,
109
DEFAULT_SANDBOX_CLIENT_ID,
1110
DEFAULT_REDIRECT_URI_PATH,
1211
} from './constants';
1312

1413
/**
15-
* Detect if we're in a sandbox/test environment based on the current URL.
16-
* Client-side: checks if the hostname includes 'sandbox' or 'localhost'.
17-
* Server-side: uses NODE_ENV === 'development' as fallback so server and client
18-
* use the same sandbox client ID when running locally (e.g. `next dev`).
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.
1917
*
20-
* @returns true if in sandbox environment, false otherwise
21-
*/
22-
export function isSandboxEnvironment(): boolean {
23-
if (typeof window === 'undefined') {
24-
// Server-side: NODE_ENV is set by Next.js (development in `next dev`, production in `next build` + `next start`)
25-
return process.env.NODE_ENV === 'development';
26-
}
27-
28-
const hostname = window.location.hostname.toLowerCase();
29-
return hostname.includes('sandbox') || hostname.includes('localhost');
30-
}
31-
32-
/**
33-
* Derive the default clientId based on the environment.
34-
* Uses public Immutable client IDs for sandbox and production.
35-
*
36-
* @returns Default client ID for the current environment
18+
* @returns Sandbox client ID (used when createAuthConfig is called with no args)
3719
*/
3820
export function deriveDefaultClientId(): string {
39-
return isSandboxEnvironment() ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID;
21+
return DEFAULT_SANDBOX_CLIENT_ID;
4022
}
4123

4224
/**

packages/passport/sdk-sample-app/src/lib/immutable-auth.server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ export const prodAuth = NextAuth({
3737
});
3838

3939
// Default auth (zero config): uses createAuthConfig() with no args.
40-
// Auto-detects sandbox (NODE_ENV=development) or production client ID.
41-
// Enables testing default auth with wallet and transactions.
40+
// Always uses sandbox. Enables testing default auth with wallet and transactions.
4241
export const defaultAuth = NextAuth({
4342
...createAuthConfig(),
4443
...sharedAuthOptions,

packages/passport/sdk-sample-app/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export enum EnvironmentNames {
1111
DEV = 'dev',
1212
SANDBOX = 'sandbox',
1313
PRODUCTION = 'production',
14-
/** Zero-config auth: uses createAuthConfig() with no args, auto-detects sandbox/production */
14+
/** Zero-config auth: uses createAuthConfig() with no args, always sandbox */
1515
DEFAULT = 'default',
1616
}
1717

0 commit comments

Comments
 (0)