@imtbl/auth-next-server 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 +51 -5
- package/dist/node/index.cjs +41 -3
- package/dist/node/index.js +31 -3
- package/dist/types/config.d.ts +20 -7
- package/dist/types/constants.d.ts +17 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +3 -3
- package/src/config.ts +44 -13
- package/src/constants.ts +21 -0
- package/src/index.ts +12 -0
package/README.md
CHANGED
|
@@ -30,6 +30,16 @@ yarn add @imtbl/auth-next-server next-auth@5
|
|
|
30
30
|
- `next` >= 14.0.0
|
|
31
31
|
- `next-auth` >= 5.0.0-beta.25
|
|
32
32
|
|
|
33
|
+
### Next.js 14 Compatibility
|
|
34
|
+
|
|
35
|
+
This package is compatible with both Next.js 14 and 15. It uses only standard APIs available in both versions:
|
|
36
|
+
|
|
37
|
+
- `next/server`: `NextRequest`, `NextResponse` (middleware)
|
|
38
|
+
- `next/navigation`: `redirect` (Server Components)
|
|
39
|
+
- NextAuth v5 with App Router
|
|
40
|
+
|
|
41
|
+
No Next.js 15-only APIs are used (e.g. async `headers()`/`cookies()`, `unstable_after`).
|
|
42
|
+
|
|
33
43
|
## Quick Start
|
|
34
44
|
|
|
35
45
|
### 1. Create Auth Configuration
|
|
@@ -69,19 +79,55 @@ NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
|
|
69
79
|
AUTH_SECRET=your-secret-key-min-32-characters
|
|
70
80
|
```
|
|
71
81
|
|
|
82
|
+
## Default Auth (Zero Config)
|
|
83
|
+
|
|
84
|
+
Policy: **provide nothing → full sandbox; provide config → provide everything.**
|
|
85
|
+
|
|
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.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// lib/auth.ts
|
|
94
|
+
import NextAuth from "next-auth";
|
|
95
|
+
import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
96
|
+
|
|
97
|
+
// Zero config - only AUTH_SECRET required in .env
|
|
98
|
+
export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig());
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
With partial overrides:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// With config - provide clientId and redirectUri
|
|
105
|
+
export const { handlers, auth, signIn, signOut } = NextAuth(
|
|
106
|
+
createAuthConfig({
|
|
107
|
+
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
108
|
+
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> **Note:** Default auth uses public Immutable client IDs. For production apps, use your own client ID from Immutable Hub.
|
|
114
|
+
|
|
72
115
|
## Configuration
|
|
73
116
|
|
|
74
|
-
### `createAuthConfig(config)`
|
|
117
|
+
### `createAuthConfig(config?)`
|
|
75
118
|
|
|
76
|
-
Creates an Auth.js v5 configuration object for Immutable authentication.
|
|
119
|
+
Creates an Auth.js v5 configuration object for Immutable authentication. Config is optional—when omitted, sensible defaults are used. Pass this to `NextAuth()` to create your auth instance.
|
|
77
120
|
|
|
78
121
|
```typescript
|
|
79
122
|
import NextAuth from "next-auth";
|
|
80
123
|
import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
81
124
|
|
|
125
|
+
// Zero config
|
|
126
|
+
const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig());
|
|
127
|
+
|
|
128
|
+
// Or with custom config
|
|
82
129
|
const { handlers, auth, signIn, signOut } = NextAuth(
|
|
83
130
|
createAuthConfig({
|
|
84
|
-
// Required
|
|
85
131
|
clientId: "your-client-id",
|
|
86
132
|
redirectUri: "https://your-app.com/callback",
|
|
87
133
|
|
|
@@ -97,8 +143,8 @@ const { handlers, auth, signIn, signOut } = NextAuth(
|
|
|
97
143
|
|
|
98
144
|
| Option | Type | Required | Description |
|
|
99
145
|
| ---------------------- | -------- | -------- | ------------------------------------------------------------------------ |
|
|
100
|
-
| `clientId` | `string` | Yes
|
|
101
|
-
| `redirectUri` | `string` | Yes
|
|
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) |
|
|
102
148
|
| `audience` | `string` | No | OAuth audience (default: `"platform_api"`) |
|
|
103
149
|
| `scope` | `string` | No | OAuth scopes (default: `"openid profile email offline_access transact"`) |
|
|
104
150
|
| `authenticationDomain` | `string` | No | Auth domain (default: `"https://auth.immutable.com"`) |
|
package/dist/node/index.cjs
CHANGED
|
@@ -30,7 +30,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
DEFAULT_AUDIENCE: () => DEFAULT_AUDIENCE,
|
|
34
|
+
DEFAULT_AUTH_DOMAIN: () => DEFAULT_AUTH_DOMAIN,
|
|
35
|
+
DEFAULT_LOGOUT_REDIRECT_URI_PATH: () => DEFAULT_LOGOUT_REDIRECT_URI_PATH,
|
|
36
|
+
DEFAULT_NEXTAUTH_BASE_PATH: () => DEFAULT_NEXTAUTH_BASE_PATH,
|
|
37
|
+
DEFAULT_REDIRECT_URI_PATH: () => DEFAULT_REDIRECT_URI_PATH,
|
|
38
|
+
DEFAULT_SANDBOX_CLIENT_ID: () => DEFAULT_SANDBOX_CLIENT_ID,
|
|
39
|
+
DEFAULT_SCOPE: () => DEFAULT_SCOPE,
|
|
40
|
+
DEFAULT_TOKEN_EXPIRY_MS: () => DEFAULT_TOKEN_EXPIRY_MS,
|
|
41
|
+
IMMUTABLE_PROVIDER_ID: () => IMMUTABLE_PROVIDER_ID,
|
|
33
42
|
NextAuth: () => import_next_auth.default,
|
|
43
|
+
TOKEN_EXPIRY_BUFFER_MS: () => TOKEN_EXPIRY_BUFFER_MS,
|
|
34
44
|
createAuthConfig: () => createAuthConfig,
|
|
35
45
|
createAuthMiddleware: () => createAuthMiddleware,
|
|
36
46
|
createAuthOptions: () => createAuthOptions,
|
|
@@ -64,11 +74,18 @@ var import_jwt = require("next-auth/jwt");
|
|
|
64
74
|
|
|
65
75
|
// src/constants.ts
|
|
66
76
|
var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
|
|
77
|
+
var DEFAULT_AUDIENCE = "platform_api";
|
|
78
|
+
var DEFAULT_SCOPE = "openid profile email offline_access transact";
|
|
67
79
|
var IMMUTABLE_PROVIDER_ID = "immutable";
|
|
80
|
+
var DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
|
|
68
81
|
var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
|
|
69
82
|
var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
|
|
70
83
|
var TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
84
|
+
var TOKEN_EXPIRY_BUFFER_MS = TOKEN_EXPIRY_BUFFER_SECONDS * 1e3;
|
|
71
85
|
var DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
86
|
+
var DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
|
|
87
|
+
var DEFAULT_REDIRECT_URI_PATH = "/callback";
|
|
88
|
+
var DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
|
|
72
89
|
|
|
73
90
|
// src/refresh.ts
|
|
74
91
|
function isTokenExpired(accessTokenExpires, bufferSeconds = TOKEN_EXPIRY_BUFFER_SECONDS) {
|
|
@@ -168,8 +185,19 @@ async function validateTokens(accessToken, authDomain) {
|
|
|
168
185
|
return null;
|
|
169
186
|
}
|
|
170
187
|
}
|
|
188
|
+
function resolveDefaultRedirectUri() {
|
|
189
|
+
const origin = process.env.__NEXT_PRIVATE_ORIGIN;
|
|
190
|
+
if (origin) {
|
|
191
|
+
return new URL(DEFAULT_REDIRECT_URI_PATH, origin).href;
|
|
192
|
+
}
|
|
193
|
+
return DEFAULT_REDIRECT_URI_PATH;
|
|
194
|
+
}
|
|
171
195
|
function createAuthConfig(config) {
|
|
172
|
-
const
|
|
196
|
+
const resolvedConfig = config ?? {
|
|
197
|
+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
|
|
198
|
+
redirectUri: resolveDefaultRedirectUri()
|
|
199
|
+
};
|
|
200
|
+
const authDomain = resolvedConfig.authenticationDomain || DEFAULT_AUTH_DOMAIN;
|
|
173
201
|
return {
|
|
174
202
|
// Custom jwt.encode: strip idToken from the cookie to reduce size and avoid
|
|
175
203
|
// CloudFront 413 "Request Entity Too Large" errors. The idToken (~1-2 KB) is
|
|
@@ -265,7 +293,7 @@ function createAuthConfig(config) {
|
|
|
265
293
|
try {
|
|
266
294
|
const refreshed = await refreshAccessToken(
|
|
267
295
|
token.refreshToken,
|
|
268
|
-
|
|
296
|
+
resolvedConfig.clientId,
|
|
269
297
|
authDomain
|
|
270
298
|
);
|
|
271
299
|
const zkEvm = extractZkEvmFromIdToken(refreshed.idToken);
|
|
@@ -305,7 +333,7 @@ function createAuthConfig(config) {
|
|
|
305
333
|
try {
|
|
306
334
|
const refreshed = await refreshAccessToken(
|
|
307
335
|
token.refreshToken,
|
|
308
|
-
|
|
336
|
+
resolvedConfig.clientId,
|
|
309
337
|
authDomain
|
|
310
338
|
);
|
|
311
339
|
const zkEvm = extractZkEvmFromIdToken(refreshed.idToken);
|
|
@@ -509,7 +537,17 @@ function withAuth(auth, handler) {
|
|
|
509
537
|
}
|
|
510
538
|
// Annotate the CommonJS export names for ESM import in node:
|
|
511
539
|
0 && (module.exports = {
|
|
540
|
+
DEFAULT_AUDIENCE,
|
|
541
|
+
DEFAULT_AUTH_DOMAIN,
|
|
542
|
+
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
|
|
543
|
+
DEFAULT_NEXTAUTH_BASE_PATH,
|
|
544
|
+
DEFAULT_REDIRECT_URI_PATH,
|
|
545
|
+
DEFAULT_SANDBOX_CLIENT_ID,
|
|
546
|
+
DEFAULT_SCOPE,
|
|
547
|
+
DEFAULT_TOKEN_EXPIRY_MS,
|
|
548
|
+
IMMUTABLE_PROVIDER_ID,
|
|
512
549
|
NextAuth,
|
|
550
|
+
TOKEN_EXPIRY_BUFFER_MS,
|
|
513
551
|
createAuthConfig,
|
|
514
552
|
createAuthMiddleware,
|
|
515
553
|
createAuthOptions,
|
package/dist/node/index.js
CHANGED
|
@@ -17,11 +17,18 @@ import { encode as encodeImport } from "next-auth/jwt";
|
|
|
17
17
|
|
|
18
18
|
// src/constants.ts
|
|
19
19
|
var DEFAULT_AUTH_DOMAIN = "https://auth.immutable.com";
|
|
20
|
+
var DEFAULT_AUDIENCE = "platform_api";
|
|
21
|
+
var DEFAULT_SCOPE = "openid profile email offline_access transact";
|
|
20
22
|
var IMMUTABLE_PROVIDER_ID = "immutable";
|
|
23
|
+
var DEFAULT_NEXTAUTH_BASE_PATH = "/api/auth";
|
|
21
24
|
var DEFAULT_TOKEN_EXPIRY_SECONDS = 900;
|
|
22
25
|
var DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1e3;
|
|
23
26
|
var TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
27
|
+
var TOKEN_EXPIRY_BUFFER_MS = TOKEN_EXPIRY_BUFFER_SECONDS * 1e3;
|
|
24
28
|
var DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
29
|
+
var DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
|
|
30
|
+
var DEFAULT_REDIRECT_URI_PATH = "/callback";
|
|
31
|
+
var DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
|
|
25
32
|
|
|
26
33
|
// src/refresh.ts
|
|
27
34
|
function isTokenExpired(accessTokenExpires, bufferSeconds = TOKEN_EXPIRY_BUFFER_SECONDS) {
|
|
@@ -121,8 +128,19 @@ async function validateTokens(accessToken, authDomain) {
|
|
|
121
128
|
return null;
|
|
122
129
|
}
|
|
123
130
|
}
|
|
131
|
+
function resolveDefaultRedirectUri() {
|
|
132
|
+
const origin = process.env.__NEXT_PRIVATE_ORIGIN;
|
|
133
|
+
if (origin) {
|
|
134
|
+
return new URL(DEFAULT_REDIRECT_URI_PATH, origin).href;
|
|
135
|
+
}
|
|
136
|
+
return DEFAULT_REDIRECT_URI_PATH;
|
|
137
|
+
}
|
|
124
138
|
function createAuthConfig(config) {
|
|
125
|
-
const
|
|
139
|
+
const resolvedConfig = config ?? {
|
|
140
|
+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
|
|
141
|
+
redirectUri: resolveDefaultRedirectUri()
|
|
142
|
+
};
|
|
143
|
+
const authDomain = resolvedConfig.authenticationDomain || DEFAULT_AUTH_DOMAIN;
|
|
126
144
|
return {
|
|
127
145
|
// Custom jwt.encode: strip idToken from the cookie to reduce size and avoid
|
|
128
146
|
// CloudFront 413 "Request Entity Too Large" errors. The idToken (~1-2 KB) is
|
|
@@ -218,7 +236,7 @@ function createAuthConfig(config) {
|
|
|
218
236
|
try {
|
|
219
237
|
const refreshed = await refreshAccessToken(
|
|
220
238
|
token.refreshToken,
|
|
221
|
-
|
|
239
|
+
resolvedConfig.clientId,
|
|
222
240
|
authDomain
|
|
223
241
|
);
|
|
224
242
|
const zkEvm = extractZkEvmFromIdToken(refreshed.idToken);
|
|
@@ -258,7 +276,7 @@ function createAuthConfig(config) {
|
|
|
258
276
|
try {
|
|
259
277
|
const refreshed = await refreshAccessToken(
|
|
260
278
|
token.refreshToken,
|
|
261
|
-
|
|
279
|
+
resolvedConfig.clientId,
|
|
262
280
|
authDomain
|
|
263
281
|
);
|
|
264
282
|
const zkEvm = extractZkEvmFromIdToken(refreshed.idToken);
|
|
@@ -461,7 +479,17 @@ function withAuth(auth, handler) {
|
|
|
461
479
|
};
|
|
462
480
|
}
|
|
463
481
|
export {
|
|
482
|
+
DEFAULT_AUDIENCE,
|
|
483
|
+
DEFAULT_AUTH_DOMAIN,
|
|
484
|
+
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
|
|
485
|
+
DEFAULT_NEXTAUTH_BASE_PATH,
|
|
486
|
+
DEFAULT_REDIRECT_URI_PATH,
|
|
487
|
+
DEFAULT_SANDBOX_CLIENT_ID,
|
|
488
|
+
DEFAULT_SCOPE,
|
|
489
|
+
DEFAULT_TOKEN_EXPIRY_MS,
|
|
490
|
+
IMMUTABLE_PROVIDER_ID,
|
|
464
491
|
default2 as NextAuth,
|
|
492
|
+
TOKEN_EXPIRY_BUFFER_MS,
|
|
465
493
|
createAuthConfig,
|
|
466
494
|
createAuthMiddleware,
|
|
467
495
|
createAuthOptions,
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import type { NextAuthConfig } from 'next-auth';
|
|
2
2
|
import type { ImmutableAuthConfig } from './types';
|
|
3
3
|
/**
|
|
4
|
-
* Create Auth.js v5 configuration for Immutable authentication
|
|
4
|
+
* Create Auth.js v5 configuration for Immutable authentication.
|
|
5
|
+
*
|
|
6
|
+
* Policy: provide nothing → full sandbox config; provide config → provide everything.
|
|
7
|
+
* - Zero config: sandbox clientId, auto-derived redirectUri. No conflicts.
|
|
8
|
+
* - With config: clientId and redirectUri required. Pass full config to avoid conflicts.
|
|
9
|
+
*
|
|
10
|
+
* @param config - Optional. When omitted, uses sandbox defaults. When provided, clientId and redirectUri are required.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Zero config - sandbox, only AUTH_SECRET required in .env
|
|
15
|
+
* import NextAuth from "next-auth";
|
|
16
|
+
* import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
17
|
+
*
|
|
18
|
+
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig());
|
|
19
|
+
* ```
|
|
5
20
|
*
|
|
6
21
|
* @example
|
|
7
22
|
* ```typescript
|
|
8
|
-
* //
|
|
23
|
+
* // With config - provide clientId and redirectUri (and optionally audience, scope, authenticationDomain)
|
|
9
24
|
* import NextAuth from "next-auth";
|
|
10
25
|
* import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
11
26
|
*
|
|
12
|
-
* const
|
|
27
|
+
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig({
|
|
13
28
|
* clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
14
29
|
* redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
15
|
-
* };
|
|
16
|
-
*
|
|
17
|
-
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig(config));
|
|
30
|
+
* }));
|
|
18
31
|
* ```
|
|
19
32
|
*/
|
|
20
|
-
export declare function createAuthConfig(config
|
|
33
|
+
export declare function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig;
|
|
21
34
|
export declare const createAuthOptions: typeof createAuthConfig;
|
|
@@ -35,8 +35,25 @@ export declare const DEFAULT_TOKEN_EXPIRY_MS: number;
|
|
|
35
35
|
* Tokens will be refreshed when they expire within this window
|
|
36
36
|
*/
|
|
37
37
|
export declare const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
38
|
+
/**
|
|
39
|
+
* Buffer time in milliseconds before token expiry to trigger refresh.
|
|
40
|
+
* Used by auth-next-client for client-side refresh timing.
|
|
41
|
+
*/
|
|
42
|
+
export declare const TOKEN_EXPIRY_BUFFER_MS: number;
|
|
38
43
|
/**
|
|
39
44
|
* Default session max age in seconds (365 days)
|
|
40
45
|
* This is how long the NextAuth session cookie will be valid
|
|
41
46
|
*/
|
|
42
47
|
export declare const DEFAULT_SESSION_MAX_AGE_SECONDS: number;
|
|
48
|
+
/**
|
|
49
|
+
* Sandbox client ID for auth-next zero-config.
|
|
50
|
+
*/
|
|
51
|
+
export declare const DEFAULT_SANDBOX_CLIENT_ID = "mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo";
|
|
52
|
+
/**
|
|
53
|
+
* Default redirect URI path for sandbox zero-config.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEFAULT_REDIRECT_URI_PATH = "/callback";
|
|
56
|
+
/**
|
|
57
|
+
* Default logout redirect URI path
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEFAULT_LOGOUT_REDIRECT_URI_PATH = "/";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { type NextRequest, NextResponse } from 'next/server';
|
|
|
23
23
|
export { default as NextAuth } from 'next-auth';
|
|
24
24
|
export { createAuthConfig, createAuthOptions } from './config';
|
|
25
25
|
export { isTokenExpired, refreshAccessToken, extractZkEvmFromIdToken, type RefreshedTokens, type ZkEvmData, } from './refresh';
|
|
26
|
+
export { DEFAULT_AUTH_DOMAIN, DEFAULT_AUDIENCE, DEFAULT_SCOPE, IMMUTABLE_PROVIDER_ID, DEFAULT_NEXTAUTH_BASE_PATH, DEFAULT_SANDBOX_CLIENT_ID, DEFAULT_REDIRECT_URI_PATH, DEFAULT_LOGOUT_REDIRECT_URI_PATH, DEFAULT_TOKEN_EXPIRY_MS, TOKEN_EXPIRY_BUFFER_MS, } from './constants';
|
|
26
27
|
export type { ImmutableAuthConfig, ImmutableTokenData, UserInfoResponse, ZkEvmUser, ImmutableUser, } from './types';
|
|
27
28
|
/**
|
|
28
29
|
* Result from getValidSession indicating auth state
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtbl/auth-next-server",
|
|
3
|
-
"version": "2.12.7
|
|
3
|
+
"version": "2.12.7",
|
|
4
4
|
"description": "Immutable Auth.js v5 integration for Next.js - Server-side utilities",
|
|
5
5
|
"author": "Immutable",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"next": "^15.0.0",
|
|
30
|
+
"next": "^14.0.0 || ^15.0.0",
|
|
31
31
|
"next-auth": "^5.0.0-beta.25"
|
|
32
32
|
},
|
|
33
33
|
"peerDependenciesMeta": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@types/node": "^22.10.7",
|
|
46
46
|
"eslint": "^8.56.0",
|
|
47
47
|
"jest": "^29.7.0",
|
|
48
|
-
"next": "^15.
|
|
48
|
+
"next": "^15.2.6",
|
|
49
49
|
"next-auth": "^5.0.0-beta.30",
|
|
50
50
|
"tsup": "^8.3.0",
|
|
51
51
|
"typescript": "^5.6.2"
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,8 @@ import type { ImmutableAuthConfig, ImmutableTokenData, UserInfoResponse } from '
|
|
|
8
8
|
import { isTokenExpired, refreshAccessToken, extractZkEvmFromIdToken } from './refresh';
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_AUTH_DOMAIN,
|
|
11
|
+
DEFAULT_REDIRECT_URI_PATH,
|
|
12
|
+
DEFAULT_SANDBOX_CLIENT_ID,
|
|
11
13
|
IMMUTABLE_PROVIDER_ID,
|
|
12
14
|
DEFAULT_SESSION_MAX_AGE_SECONDS,
|
|
13
15
|
} from './constants';
|
|
@@ -55,24 +57,54 @@ async function validateTokens(
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
60
|
+
* Resolve redirect URI for zero-config mode.
|
|
61
|
+
* Uses __NEXT_PRIVATE_ORIGIN when available (Next.js internal), otherwise path-only.
|
|
62
|
+
*/
|
|
63
|
+
function resolveDefaultRedirectUri(): string {
|
|
64
|
+
// eslint-disable-next-line no-underscore-dangle -- Next.js internal env var
|
|
65
|
+
const origin = process.env.__NEXT_PRIVATE_ORIGIN;
|
|
66
|
+
if (origin) {
|
|
67
|
+
return new URL(DEFAULT_REDIRECT_URI_PATH, origin).href;
|
|
68
|
+
}
|
|
69
|
+
return DEFAULT_REDIRECT_URI_PATH;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create Auth.js v5 configuration for Immutable authentication.
|
|
74
|
+
*
|
|
75
|
+
* Policy: provide nothing → full sandbox config; provide config → provide everything.
|
|
76
|
+
* - Zero config: sandbox clientId, auto-derived redirectUri. No conflicts.
|
|
77
|
+
* - With config: clientId and redirectUri required. Pass full config to avoid conflicts.
|
|
78
|
+
*
|
|
79
|
+
* @param config - Optional. When omitted, uses sandbox defaults. When provided, clientId and redirectUri are required.
|
|
59
80
|
*
|
|
60
81
|
* @example
|
|
61
82
|
* ```typescript
|
|
62
|
-
* //
|
|
83
|
+
* // Zero config - sandbox, only AUTH_SECRET required in .env
|
|
63
84
|
* import NextAuth from "next-auth";
|
|
64
85
|
* import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
65
86
|
*
|
|
66
|
-
* const
|
|
87
|
+
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig());
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // With config - provide clientId and redirectUri (and optionally audience, scope, authenticationDomain)
|
|
93
|
+
* import NextAuth from "next-auth";
|
|
94
|
+
* import { createAuthConfig } from "@imtbl/auth-next-server";
|
|
95
|
+
*
|
|
96
|
+
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig({
|
|
67
97
|
* clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
68
98
|
* redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
69
|
-
* };
|
|
70
|
-
*
|
|
71
|
-
* export const { handlers, auth, signIn, signOut } = NextAuth(createAuthConfig(config));
|
|
99
|
+
* }));
|
|
72
100
|
* ```
|
|
73
101
|
*/
|
|
74
|
-
export function createAuthConfig(config
|
|
75
|
-
const
|
|
102
|
+
export function createAuthConfig(config?: ImmutableAuthConfig): NextAuthConfig {
|
|
103
|
+
const resolvedConfig: ImmutableAuthConfig = config ?? {
|
|
104
|
+
clientId: DEFAULT_SANDBOX_CLIENT_ID,
|
|
105
|
+
redirectUri: resolveDefaultRedirectUri(),
|
|
106
|
+
};
|
|
107
|
+
const authDomain = resolvedConfig.authenticationDomain || DEFAULT_AUTH_DOMAIN;
|
|
76
108
|
|
|
77
109
|
return {
|
|
78
110
|
// Custom jwt.encode: strip idToken from the cookie to reduce size and avoid
|
|
@@ -85,7 +117,6 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
|
|
|
85
117
|
async encode(params) {
|
|
86
118
|
const { token, ...rest } = params;
|
|
87
119
|
if (token) {
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
89
120
|
const { idToken, ...cookieToken } = token as Record<string, unknown>;
|
|
90
121
|
return defaultJwtEncode({ ...rest, token: cookieToken });
|
|
91
122
|
}
|
|
@@ -204,7 +235,7 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
|
|
|
204
235
|
try {
|
|
205
236
|
const refreshed = await refreshAccessToken(
|
|
206
237
|
token.refreshToken as string,
|
|
207
|
-
|
|
238
|
+
resolvedConfig.clientId,
|
|
208
239
|
authDomain,
|
|
209
240
|
);
|
|
210
241
|
// Extract zkEvm claims from the refreshed idToken
|
|
@@ -219,7 +250,7 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
|
|
|
219
250
|
error: undefined,
|
|
220
251
|
};
|
|
221
252
|
} catch (error) {
|
|
222
|
-
|
|
253
|
+
// eslint-disable-next-line no-console
|
|
223
254
|
console.error('[auth-next-server] Force refresh failed:', error);
|
|
224
255
|
return {
|
|
225
256
|
...token,
|
|
@@ -252,7 +283,7 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
|
|
|
252
283
|
try {
|
|
253
284
|
const refreshed = await refreshAccessToken(
|
|
254
285
|
token.refreshToken as string,
|
|
255
|
-
|
|
286
|
+
resolvedConfig.clientId,
|
|
256
287
|
authDomain,
|
|
257
288
|
);
|
|
258
289
|
// Extract zkEvm claims from the refreshed idToken
|
|
@@ -267,7 +298,7 @@ export function createAuthConfig(config: ImmutableAuthConfig): NextAuthConfig {
|
|
|
267
298
|
error: undefined, // Clear any previous error
|
|
268
299
|
};
|
|
269
300
|
} catch (error) {
|
|
270
|
-
|
|
301
|
+
// eslint-disable-next-line no-console
|
|
271
302
|
console.error('[auth-next-server] Token refresh failed:', error);
|
|
272
303
|
return {
|
|
273
304
|
...token,
|
package/src/constants.ts
CHANGED
|
@@ -44,8 +44,29 @@ export const DEFAULT_TOKEN_EXPIRY_MS = DEFAULT_TOKEN_EXPIRY_SECONDS * 1000;
|
|
|
44
44
|
*/
|
|
45
45
|
export const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Buffer time in milliseconds before token expiry to trigger refresh.
|
|
49
|
+
* Used by auth-next-client for client-side refresh timing.
|
|
50
|
+
*/
|
|
51
|
+
export const TOKEN_EXPIRY_BUFFER_MS = TOKEN_EXPIRY_BUFFER_SECONDS * 1000;
|
|
52
|
+
|
|
47
53
|
/**
|
|
48
54
|
* Default session max age in seconds (365 days)
|
|
49
55
|
* This is how long the NextAuth session cookie will be valid
|
|
50
56
|
*/
|
|
51
57
|
export const DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sandbox client ID for auth-next zero-config.
|
|
61
|
+
*/
|
|
62
|
+
export const DEFAULT_SANDBOX_CLIENT_ID = 'mjtCL8mt06BtbxSkp2vbrYStKWnXVZfo';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Default redirect URI path for sandbox zero-config.
|
|
66
|
+
*/
|
|
67
|
+
export const DEFAULT_REDIRECT_URI_PATH = '/callback';
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Default logout redirect URI path
|
|
71
|
+
*/
|
|
72
|
+
export const DEFAULT_LOGOUT_REDIRECT_URI_PATH = '/';
|
package/src/index.ts
CHANGED
|
@@ -45,6 +45,18 @@ export {
|
|
|
45
45
|
type RefreshedTokens,
|
|
46
46
|
type ZkEvmData,
|
|
47
47
|
} 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_SANDBOX_CLIENT_ID,
|
|
55
|
+
DEFAULT_REDIRECT_URI_PATH,
|
|
56
|
+
DEFAULT_LOGOUT_REDIRECT_URI_PATH,
|
|
57
|
+
DEFAULT_TOKEN_EXPIRY_MS,
|
|
58
|
+
TOKEN_EXPIRY_BUFFER_MS,
|
|
59
|
+
} from './constants';
|
|
48
60
|
|
|
49
61
|
// ============================================================================
|
|
50
62
|
// Type exports
|