@imtbl/auth-nextjs 0.0.1-alpha.0 → 2.12.4-alpha.11
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 +307 -127
- package/dist/node/chunk-Z7KVQQTY.js +276 -0
- package/dist/node/client/index.cjs +445 -0
- package/dist/node/client/index.js +431 -0
- package/dist/node/index.cjs +319 -0
- package/dist/node/index.js +24 -0
- package/dist/node/server/index.cjs +353 -0
- package/dist/node/server/index.js +58 -0
- package/dist/types/client/callback.d.ts +71 -0
- package/dist/types/client/index.d.ts +5 -0
- package/dist/types/client/provider.d.ts +88 -0
- package/dist/types/config.d.ts +21 -0
- package/dist/types/constants.d.ts +42 -0
- package/dist/types/index.d.ts +77 -0
- package/dist/types/refresh.d.ts +16 -0
- package/dist/types/server/index.d.ts +104 -0
- package/dist/types/types.d.ts +198 -0
- package/dist/types/utils/token.d.ts +8 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,38 +1,58 @@
|
|
|
1
1
|
# @imtbl/auth-nextjs
|
|
2
2
|
|
|
3
|
-
Next.js authentication integration for Immutable SDK using
|
|
3
|
+
Next.js App Router authentication integration for Immutable SDK using Auth.js v5.
|
|
4
4
|
|
|
5
|
-
This package bridges `@imtbl/auth` popup-based authentication with
|
|
5
|
+
This package bridges `@imtbl/auth` popup-based authentication with Auth.js session management, providing:
|
|
6
6
|
|
|
7
7
|
- Server-side session storage in encrypted JWT cookies
|
|
8
8
|
- Automatic token refresh on both server and client
|
|
9
|
-
- Full SSR support with `
|
|
9
|
+
- Full SSR support with `auth()` function
|
|
10
10
|
- React hooks for easy client-side authentication
|
|
11
|
+
- Middleware support for protecting routes
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Next.js 14+ with App Router
|
|
16
|
+
- Auth.js v5 (next-auth@5.x)
|
|
17
|
+
- React 18+
|
|
11
18
|
|
|
12
19
|
## Installation
|
|
13
20
|
|
|
14
21
|
```bash
|
|
15
|
-
pnpm add @imtbl/auth-nextjs next-auth
|
|
22
|
+
pnpm add @imtbl/auth-nextjs next-auth@beta
|
|
16
23
|
```
|
|
17
24
|
|
|
18
25
|
## Quick Start
|
|
19
26
|
|
|
20
|
-
### 1.
|
|
27
|
+
### 1. Create Auth Configuration
|
|
21
28
|
|
|
22
29
|
```typescript
|
|
23
|
-
//
|
|
24
|
-
import {
|
|
30
|
+
// lib/auth.ts
|
|
31
|
+
import { createImmutableAuth } from "@imtbl/auth-nextjs";
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
const config = {
|
|
27
34
|
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
28
35
|
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
29
|
-
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const { handlers, auth, signIn, signOut } = createImmutableAuth(config);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Set Up Auth API Route
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// app/api/auth/[...nextauth]/route.ts
|
|
45
|
+
import { handlers } from "@/lib/auth";
|
|
46
|
+
|
|
47
|
+
export const { GET, POST } = handlers;
|
|
30
48
|
```
|
|
31
49
|
|
|
32
|
-
###
|
|
50
|
+
### 3. Create Callback Page
|
|
33
51
|
|
|
34
52
|
```typescript
|
|
35
|
-
//
|
|
53
|
+
// app/callback/page.tsx
|
|
54
|
+
"use client";
|
|
55
|
+
|
|
36
56
|
import { CallbackPage } from "@imtbl/auth-nextjs/client";
|
|
37
57
|
|
|
38
58
|
const config = {
|
|
@@ -41,14 +61,18 @@ const config = {
|
|
|
41
61
|
};
|
|
42
62
|
|
|
43
63
|
export default function Callback() {
|
|
44
|
-
return <CallbackPage config={config} />;
|
|
64
|
+
return <CallbackPage config={config} redirectTo="/dashboard" />;
|
|
45
65
|
}
|
|
46
66
|
```
|
|
47
67
|
|
|
48
|
-
|
|
68
|
+
See [CallbackPage Props](#callbackpage-props) for all available options.
|
|
69
|
+
|
|
70
|
+
### 4. Add Provider to Layout
|
|
49
71
|
|
|
50
72
|
```typescript
|
|
51
|
-
//
|
|
73
|
+
// app/providers.tsx
|
|
74
|
+
"use client";
|
|
75
|
+
|
|
52
76
|
import { ImmutableAuthProvider } from "@imtbl/auth-nextjs/client";
|
|
53
77
|
|
|
54
78
|
const config = {
|
|
@@ -56,21 +80,39 @@ const config = {
|
|
|
56
80
|
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
57
81
|
};
|
|
58
82
|
|
|
59
|
-
export
|
|
83
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
60
84
|
return (
|
|
61
|
-
<ImmutableAuthProvider config={config}
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
<ImmutableAuthProvider config={config}>{children}</ImmutableAuthProvider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// app/layout.tsx
|
|
90
|
+
import { Providers } from "./providers";
|
|
91
|
+
|
|
92
|
+
export default function RootLayout({
|
|
93
|
+
children,
|
|
94
|
+
}: {
|
|
95
|
+
children: React.ReactNode;
|
|
96
|
+
}) {
|
|
97
|
+
return (
|
|
98
|
+
<html>
|
|
99
|
+
<body>
|
|
100
|
+
<Providers>{children}</Providers>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
64
103
|
);
|
|
65
104
|
}
|
|
66
105
|
```
|
|
67
106
|
|
|
68
|
-
###
|
|
107
|
+
### 5. Use in Components
|
|
69
108
|
|
|
70
109
|
```typescript
|
|
110
|
+
// app/components/LoginButton.tsx
|
|
111
|
+
"use client";
|
|
112
|
+
|
|
71
113
|
import { useImmutableAuth } from "@imtbl/auth-nextjs/client";
|
|
72
114
|
|
|
73
|
-
function LoginButton() {
|
|
115
|
+
export function LoginButton() {
|
|
74
116
|
const { user, isLoading, signIn, signOut } = useImmutableAuth();
|
|
75
117
|
|
|
76
118
|
if (isLoading) return <div>Loading...</div>;
|
|
@@ -84,66 +126,65 @@ function LoginButton() {
|
|
|
84
126
|
);
|
|
85
127
|
}
|
|
86
128
|
|
|
87
|
-
return <button onClick={signIn}>Login with Immutable</button>;
|
|
129
|
+
return <button onClick={() => signIn()}>Login with Immutable</button>;
|
|
88
130
|
}
|
|
89
131
|
```
|
|
90
132
|
|
|
91
|
-
###
|
|
133
|
+
### 6. Access Session in Server Components
|
|
92
134
|
|
|
93
135
|
```typescript
|
|
94
|
-
//
|
|
95
|
-
import {
|
|
96
|
-
import
|
|
136
|
+
// app/profile/page.tsx
|
|
137
|
+
import { auth } from "@/lib/auth";
|
|
138
|
+
import { redirect } from "next/navigation";
|
|
97
139
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
101
|
-
};
|
|
140
|
+
export default async function ProfilePage() {
|
|
141
|
+
const session = await auth();
|
|
102
142
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
143
|
+
if (!session) {
|
|
144
|
+
redirect("/login");
|
|
145
|
+
}
|
|
107
146
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return { props: { user: session?.user ?? null } };
|
|
111
|
-
};
|
|
147
|
+
return <h1>Welcome, {session.user.email}</h1>;
|
|
148
|
+
}
|
|
112
149
|
```
|
|
113
150
|
|
|
114
|
-
###
|
|
151
|
+
### 7. Protect Routes with Middleware (Optional)
|
|
115
152
|
|
|
116
153
|
```typescript
|
|
117
|
-
//
|
|
118
|
-
import {
|
|
119
|
-
|
|
120
|
-
const config = {
|
|
121
|
-
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
122
|
-
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
function DashboardPage() {
|
|
126
|
-
return <h1>Dashboard (protected)</h1>;
|
|
127
|
-
}
|
|
154
|
+
// middleware.ts
|
|
155
|
+
import { createAuthMiddleware } from "@imtbl/auth-nextjs/server";
|
|
156
|
+
import { auth } from "@/lib/auth";
|
|
128
157
|
|
|
129
|
-
export default
|
|
158
|
+
export default createAuthMiddleware(auth, {
|
|
159
|
+
loginUrl: "/login",
|
|
160
|
+
});
|
|
130
161
|
|
|
131
|
-
|
|
132
|
-
|
|
162
|
+
export const config = {
|
|
163
|
+
matcher: ["/dashboard/:path*", "/profile/:path*"],
|
|
164
|
+
};
|
|
133
165
|
```
|
|
134
166
|
|
|
135
167
|
## Configuration Options
|
|
136
168
|
|
|
137
169
|
The `ImmutableAuthConfig` object accepts the following properties:
|
|
138
170
|
|
|
139
|
-
| Property | Type | Required | Default | Description
|
|
140
|
-
| ---------------------- | -------- | -------- | ------------------------------------------------ |
|
|
141
|
-
| `clientId` | `string` | Yes | - | Immutable OAuth client ID
|
|
142
|
-
| `redirectUri` | `string` | Yes | - | OAuth callback redirect URI
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
171
|
+
| Property | Type | Required | Default | Description |
|
|
172
|
+
| ---------------------- | -------- | -------- | ------------------------------------------------ | -------------------------------------------------------------- |
|
|
173
|
+
| `clientId` | `string` | Yes | - | Immutable OAuth client ID |
|
|
174
|
+
| `redirectUri` | `string` | Yes | - | OAuth callback redirect URI (for redirect flow) |
|
|
175
|
+
| `popupRedirectUri` | `string` | No | `redirectUri` | OAuth callback redirect URI for popup flow |
|
|
176
|
+
| `logoutRedirectUri` | `string` | No | - | Where to redirect after logout |
|
|
177
|
+
| `audience` | `string` | No | `"platform_api"` | OAuth audience |
|
|
178
|
+
| `scope` | `string` | No | `"openid profile email offline_access transact"` | OAuth scopes |
|
|
179
|
+
| `authenticationDomain` | `string` | No | `"https://auth.immutable.com"` | Authentication domain |
|
|
180
|
+
| `passportDomain` | `string` | No | `"https://passport.immutable.com"` | Passport domain for transaction confirmations (see note below) |
|
|
181
|
+
|
|
182
|
+
> **Important:** The `passportDomain` must match your target environment for transaction signing to work correctly:
|
|
183
|
+
>
|
|
184
|
+
> - **Production:** `https://passport.immutable.com` (default)
|
|
185
|
+
> - **Sandbox:** `https://passport.sandbox.immutable.com`
|
|
186
|
+
>
|
|
187
|
+
> If you're using the sandbox environment, you must explicitly set `passportDomain` to the sandbox URL.
|
|
147
188
|
|
|
148
189
|
## Environment Variables
|
|
149
190
|
|
|
@@ -152,9 +193,8 @@ The `ImmutableAuthConfig` object accepts the following properties:
|
|
|
152
193
|
NEXT_PUBLIC_IMMUTABLE_CLIENT_ID=your-client-id
|
|
153
194
|
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
|
154
195
|
|
|
155
|
-
# Required by
|
|
156
|
-
|
|
157
|
-
NEXTAUTH_URL=http://localhost:3000
|
|
196
|
+
# Required by Auth.js for cookie encryption
|
|
197
|
+
AUTH_SECRET=generate-with-openssl-rand-base64-32
|
|
158
198
|
```
|
|
159
199
|
|
|
160
200
|
Generate a secret:
|
|
@@ -163,103 +203,243 @@ Generate a secret:
|
|
|
163
203
|
openssl rand -base64 32
|
|
164
204
|
```
|
|
165
205
|
|
|
206
|
+
## Sandbox vs Production Configuration
|
|
207
|
+
|
|
208
|
+
When developing or testing, you'll typically use the **Sandbox** environment. Make sure to configure `passportDomain` correctly:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// lib/auth.ts
|
|
212
|
+
|
|
213
|
+
// For SANDBOX environment
|
|
214
|
+
const sandboxConfig = {
|
|
215
|
+
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
216
|
+
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
217
|
+
passportDomain: "https://passport.sandbox.immutable.com", // Required for sandbox!
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// For PRODUCTION environment
|
|
221
|
+
const productionConfig = {
|
|
222
|
+
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
223
|
+
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
224
|
+
// passportDomain defaults to 'https://passport.immutable.com'
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Use environment variable to switch between configs
|
|
228
|
+
const config =
|
|
229
|
+
process.env.NEXT_PUBLIC_IMMUTABLE_ENV === "production"
|
|
230
|
+
? productionConfig
|
|
231
|
+
: sandboxConfig;
|
|
232
|
+
|
|
233
|
+
export const { handlers, auth, signIn, signOut } = createImmutableAuth(config);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
> **Note:** The `passportDomain` is used for transaction confirmation popups. If not set correctly for your environment, transaction signing will not work as expected.
|
|
237
|
+
|
|
166
238
|
## API Reference
|
|
167
239
|
|
|
168
240
|
### Main Exports (`@imtbl/auth-nextjs`)
|
|
169
241
|
|
|
170
|
-
| Export
|
|
171
|
-
|
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
242
|
+
| Export | Description |
|
|
243
|
+
| --------------------------------------- | ------------------------------------------------------------------- |
|
|
244
|
+
| `createImmutableAuth(config, options?)` | Creates Auth.js instance with `{ handlers, auth, signIn, signOut }` |
|
|
245
|
+
| `createAuthConfig(config)` | Creates Auth.js config (for advanced use) |
|
|
246
|
+
| `refreshAccessToken(token, config)` | Utility to refresh an expired access token |
|
|
247
|
+
| `isTokenExpired(expires, buffer?)` | Utility to check if a token is expired |
|
|
175
248
|
|
|
176
249
|
**Types:**
|
|
177
250
|
|
|
178
|
-
| Type
|
|
179
|
-
|
|
|
180
|
-
| `ImmutableAuthConfig`
|
|
181
|
-
| `ImmutableAuthOverrides`
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
251
|
+
| Type | Description |
|
|
252
|
+
| ------------------------ | ----------------------------------------- |
|
|
253
|
+
| `ImmutableAuthConfig` | Configuration options |
|
|
254
|
+
| `ImmutableAuthOverrides` | Auth.js options override type |
|
|
255
|
+
| `ImmutableAuthResult` | Return type of createImmutableAuth |
|
|
256
|
+
| `ImmutableUser` | User profile type |
|
|
257
|
+
| `ImmutableTokenData` | Token data passed to credentials provider |
|
|
258
|
+
| `ZkEvmInfo` | zkEVM wallet information type |
|
|
186
259
|
|
|
187
260
|
### Client Exports (`@imtbl/auth-nextjs/client`)
|
|
188
261
|
|
|
189
|
-
| Export | Description
|
|
190
|
-
| ----------------------- |
|
|
191
|
-
| `ImmutableAuthProvider` | React context provider (wraps
|
|
192
|
-
| `useImmutableAuth()` | Hook for authentication state and methods (see below)
|
|
193
|
-
| `useAccessToken()` | Hook returning `getAccessToken` function
|
|
194
|
-
| `CallbackPage` | Pre-built callback page component for OAuth redirects
|
|
262
|
+
| Export | Description |
|
|
263
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
264
|
+
| `ImmutableAuthProvider` | React context provider (wraps Auth.js SessionProvider) |
|
|
265
|
+
| `useImmutableAuth()` | Hook for authentication state and methods (see below) |
|
|
266
|
+
| `useAccessToken()` | Hook returning `getAccessToken` function |
|
|
267
|
+
| `CallbackPage` | Pre-built callback page component for OAuth redirects |
|
|
268
|
+
|
|
269
|
+
#### CallbackPage Props
|
|
270
|
+
|
|
271
|
+
| Prop | Type | Default | Description |
|
|
272
|
+
| ------------------ | ----------------------------------------------------- | ------- | ------------------------------------------------------------------ |
|
|
273
|
+
| `config` | `ImmutableAuthConfig` | - | Required. Immutable auth configuration |
|
|
274
|
+
| `redirectTo` | `string \| ((user: ImmutableUser) => string \| void)` | `"/"` | Where to redirect after successful auth (supports dynamic routing) |
|
|
275
|
+
| `loadingComponent` | `React.ReactElement \| null` | `null` | Custom loading UI while processing authentication |
|
|
276
|
+
| `errorComponent` | `(error: string) => React.ReactElement \| null` | - | Custom error UI component |
|
|
277
|
+
| `onSuccess` | `(user: ImmutableUser) => void \| Promise<void>` | - | Callback fired after successful authentication |
|
|
278
|
+
| `onError` | `(error: string) => void` | - | Callback fired when authentication fails |
|
|
279
|
+
|
|
280
|
+
**Example with all props:**
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
// app/callback/page.tsx
|
|
284
|
+
"use client";
|
|
285
|
+
|
|
286
|
+
import { CallbackPage } from "@imtbl/auth-nextjs/client";
|
|
287
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
288
|
+
|
|
289
|
+
const config = {
|
|
290
|
+
clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
|
|
291
|
+
redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export default function Callback() {
|
|
295
|
+
return (
|
|
296
|
+
<CallbackPage
|
|
297
|
+
config={config}
|
|
298
|
+
// Dynamic redirect based on user
|
|
299
|
+
redirectTo={(user) => {
|
|
300
|
+
if (user.email?.endsWith("@admin.com")) return "/admin";
|
|
301
|
+
return "/dashboard";
|
|
302
|
+
}}
|
|
303
|
+
// Custom loading UI
|
|
304
|
+
loadingComponent={
|
|
305
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
306
|
+
<Spinner />
|
|
307
|
+
<span>Completing authentication...</span>
|
|
308
|
+
</div>
|
|
309
|
+
}
|
|
310
|
+
// Custom error UI
|
|
311
|
+
errorComponent={(error) => (
|
|
312
|
+
<div className="text-center p-8">
|
|
313
|
+
<h2 className="text-red-500">Authentication Error</h2>
|
|
314
|
+
<p>{error}</p>
|
|
315
|
+
<a href="/">Return Home</a>
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
318
|
+
// Success callback for analytics
|
|
319
|
+
onSuccess={async (user) => {
|
|
320
|
+
await analytics.track("login_success", { userId: user.sub });
|
|
321
|
+
}}
|
|
322
|
+
// Error callback for logging
|
|
323
|
+
onError={(error) => {
|
|
324
|
+
console.error("Auth failed:", error);
|
|
325
|
+
Sentry.captureMessage(error);
|
|
326
|
+
}}
|
|
327
|
+
/>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
195
331
|
|
|
196
332
|
**`useImmutableAuth()` Return Value:**
|
|
197
333
|
|
|
198
334
|
| Property | Type | Description |
|
|
199
335
|
| ----------------- | ----------------------- | ------------------------------------------------ |
|
|
200
336
|
| `user` | `ImmutableUser \| null` | Current user profile (null if not authenticated) |
|
|
201
|
-
| `session` | `Session \| null` | Full
|
|
337
|
+
| `session` | `Session \| null` | Full Auth.js session with tokens |
|
|
202
338
|
| `isLoading` | `boolean` | Whether authentication state is loading |
|
|
203
339
|
| `isAuthenticated` | `boolean` | Whether user is authenticated |
|
|
204
|
-
| `signIn` | `() => Promise
|
|
205
|
-
| `signOut` | `() => Promise<void>` | Sign out from both
|
|
340
|
+
| `signIn` | `(options?) => Promise` | Sign in with Immutable (opens popup) |
|
|
341
|
+
| `signOut` | `() => Promise<void>` | Sign out from both Auth.js and Immutable |
|
|
206
342
|
| `getAccessToken` | `() => Promise<string>` | Get a valid access token (refreshes if needed) |
|
|
207
343
|
| `auth` | `Auth \| null` | The underlying Auth instance (for advanced use) |
|
|
208
344
|
|
|
209
|
-
|
|
345
|
+
### Server Exports (`@imtbl/auth-nextjs/server`)
|
|
210
346
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `ImmutableAuthConfig` | Re-exported configuration type |
|
|
217
|
-
| `ImmutableUser` | Re-exported user type |
|
|
347
|
+
| Export | Description |
|
|
348
|
+
| ----------------------------------- | ------------------------------------------------ |
|
|
349
|
+
| `createImmutableAuth` | Re-exported for convenience |
|
|
350
|
+
| `createAuthMiddleware(auth, opts?)` | Create middleware for protecting routes |
|
|
351
|
+
| `withAuth(auth, handler)` | HOC for protecting Server Actions/Route Handlers |
|
|
218
352
|
|
|
219
|
-
|
|
353
|
+
**`createAuthMiddleware` Options:**
|
|
220
354
|
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
355
|
+
| Option | Type | Default | Description |
|
|
356
|
+
| ---------------- | ---------------------- | ---------- | -------------------------------------- |
|
|
357
|
+
| `loginUrl` | `string` | `"/login"` | URL to redirect when not authenticated |
|
|
358
|
+
| `protectedPaths` | `(string \| RegExp)[]` | - | Paths that require authentication |
|
|
359
|
+
| `publicPaths` | `(string \| RegExp)[]` | - | Paths to exclude from protection |
|
|
225
360
|
|
|
226
|
-
|
|
361
|
+
## How It Works
|
|
227
362
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
363
|
+
1. **Login**: User clicks login → `@imtbl/auth` opens popup → tokens returned
|
|
364
|
+
2. **Session Creation**: Tokens passed to Auth.js credentials provider → stored in encrypted JWT cookie
|
|
365
|
+
3. **Token Refresh**: Auth.js JWT callback automatically refreshes expired tokens using refresh_token
|
|
366
|
+
4. **SSR**: `auth()` reads and decrypts cookie, providing full session with tokens
|
|
367
|
+
5. **Auto-sync**: Tokens are automatically synced between server (NextAuth session) and client (Auth instance) to handle refresh token rotation
|
|
233
368
|
|
|
234
|
-
|
|
369
|
+
## Handling Token Refresh Errors
|
|
370
|
+
|
|
371
|
+
When a refresh token expires or becomes invalid (e.g., after 365 days of inactivity, or revoked from another session), the session will contain an `error` property. You should handle this gracefully:
|
|
372
|
+
|
|
373
|
+
### Server Components
|
|
235
374
|
|
|
236
375
|
```typescript
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
376
|
+
// app/profile/page.tsx
|
|
377
|
+
import { auth } from "@/lib/auth";
|
|
378
|
+
import { redirect } from "next/navigation";
|
|
379
|
+
|
|
380
|
+
export default async function ProfilePage() {
|
|
381
|
+
const session = await auth();
|
|
382
|
+
|
|
383
|
+
// Handle refresh token failure - prompt user to re-login
|
|
384
|
+
if (session?.error === "RefreshTokenError") {
|
|
385
|
+
redirect("/login?error=session_expired");
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!session) {
|
|
389
|
+
redirect("/login");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return <h1>Welcome, {session.user.email}</h1>;
|
|
393
|
+
}
|
|
245
394
|
```
|
|
246
395
|
|
|
247
|
-
|
|
396
|
+
### Client Components
|
|
248
397
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
| `WithPageAuthRequiredOptions` | Basic options for page protection |
|
|
252
|
-
| `WithPageAuthRequiredFullOptions` | Full options including getServerSideProps |
|
|
253
|
-
| `WithPageAuthRequiredProps` | Props added to protected pages (session) |
|
|
398
|
+
```typescript
|
|
399
|
+
"use client";
|
|
254
400
|
|
|
255
|
-
|
|
401
|
+
import { useImmutableAuth } from "@imtbl/auth-nextjs/client";
|
|
256
402
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
3. **Token Refresh**: NextAuth JWT callback automatically refreshes expired tokens using refresh_token
|
|
260
|
-
4. **SSR**: `getServerSession()` reads and decrypts cookie, providing full session with tokens
|
|
261
|
-
5. **Auto-hydration**: If localStorage is cleared but session cookie exists, the Auth instance is automatically hydrated from session tokens
|
|
403
|
+
export function ProtectedContent() {
|
|
404
|
+
const { session, user, signIn, isLoading } = useImmutableAuth();
|
|
262
405
|
|
|
263
|
-
|
|
406
|
+
if (isLoading) return <div>Loading...</div>;
|
|
264
407
|
|
|
265
|
-
|
|
408
|
+
// Handle refresh token failure
|
|
409
|
+
if (session?.error) {
|
|
410
|
+
return (
|
|
411
|
+
<div>
|
|
412
|
+
<p>Your session has expired. Please log in again.</p>
|
|
413
|
+
<button onClick={() => signIn()}>Log In</button>
|
|
414
|
+
</div>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (!user) {
|
|
419
|
+
return <button onClick={() => signIn()}>Log In</button>;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return <div>Welcome, {user.email}</div>;
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Using getAccessToken
|
|
427
|
+
|
|
428
|
+
The `getAccessToken()` function will throw an error if the token cannot be refreshed:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
const { getAccessToken } = useImmutableAuth();
|
|
432
|
+
|
|
433
|
+
async function fetchData() {
|
|
434
|
+
try {
|
|
435
|
+
const token = await getAccessToken();
|
|
436
|
+
const response = await fetch("/api/data", {
|
|
437
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
438
|
+
});
|
|
439
|
+
return response.json();
|
|
440
|
+
} catch (error) {
|
|
441
|
+
// Token refresh failed - redirect to login or show error
|
|
442
|
+
console.error("Failed to get access token:", error);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|