@chemmangat/msal-next 2.1.1 → 2.1.2

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 CHANGED
@@ -1,568 +1,551 @@
1
- # @chemmangat/msal-next
2
-
3
- Production-grade MSAL authentication library for Next.js App Router with minimal boilerplate.
4
-
5
- [![npm version](https://badge.fury.io/js/@chemmangat%2Fmsal-next.svg)](https://www.npmjs.com/package/@chemmangat/msal-next)
6
- [![npm downloads](https://img.shields.io/npm/dm/@chemmangat/msal-next.svg)](https://www.npmjs.com/package/@chemmangat/msal-next)
7
- [![bundle size](https://img.shields.io/bundlephobia/minzip/@chemmangat/msal-next)](https://bundlephobia.com/package/@chemmangat/msal-next)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
10
-
11
- ## Features
12
-
13
- âœĻ **Minimal Setup** - Just provide your `clientId` to get started
14
- 🔐 **Production Ready** - Comprehensive error handling, retry logic, and SSR support
15
- ðŸŽĻ **Beautiful Components** - Pre-styled Microsoft-branded UI components
16
- 🊝 **Powerful Hooks** - Easy-to-use hooks for auth, Graph API, and user data
17
- ðŸ›Ąïļ **Type Safe** - Full TypeScript support with generics for custom claims
18
- ⚡ **Edge Compatible** - Middleware support for protecting routes at the edge
19
- ðŸ“Ķ **Zero Config** - Sensible defaults with full customization options
20
- ðŸŠķ **Lightweight** - Only 80KB unpacked, tree-shakeable
21
-
22
- ## Why Choose This?
23
-
24
- | Feature | @chemmangat/msal-next | NextAuth.js | Manual MSAL Setup |
25
- |---------|----------------------|-------------|-------------------|
26
- | Next.js App Router | ✅ Native | ⚠ïļ Limited | ❌ Manual |
27
- | Microsoft Graph API | ✅ Built-in hooks | ❌ Manual | ❌ Manual |
28
- | TypeScript | ✅ Full support | ✅ Partial | ⚠ïļ Complex |
29
- | Setup Time | ⏱ïļ 2 minutes | ⏱ïļ 15 minutes | ⏱ïļ 60+ minutes |
30
- | Bundle Size | ðŸ“Ķ 80KB | ðŸ“Ķ 200KB+ | ðŸ“Ķ Varies |
31
- | Pre-built Components | ✅ 7 components | ❌ None | ❌ None |
32
- | Edge Middleware | ✅ Yes | ✅ Yes | ❌ Manual |
33
- | Role-Based Access | ✅ Built-in | ❌ Manual | ❌ Manual |
34
-
35
- ## Installation
36
-
37
- ```bash
38
- npm install @chemmangat/msal-next @azure/msal-browser @azure/msal-react
39
- ```
40
-
41
- ## Quick Start
42
-
43
- ### 1. Wrap your app with MsalAuthProvider
44
-
45
- ```tsx
46
- // app/layout.tsx
47
- import { MsalAuthProvider } from '@chemmangat/msal-next';
48
-
49
- export default function RootLayout({ children }) {
50
- return (
51
- <html>
52
- <body>
53
- <MsalAuthProvider clientId="YOUR_CLIENT_ID">
54
- {children}
55
- </MsalAuthProvider>
56
- </body>
57
- </html>
58
- );
59
- }
60
- ```
61
-
62
- ### 2. Add sign-in button
63
-
64
- ```tsx
65
- // app/page.tsx
66
- 'use client';
67
-
68
- import { MicrosoftSignInButton, useMsalAuth } from '@chemmangat/msal-next';
69
-
70
- export default function Home() {
71
- const { isAuthenticated } = useMsalAuth();
72
-
73
- if (isAuthenticated) {
74
- return <div>Welcome! You're signed in.</div>;
75
- }
76
-
77
- return <MicrosoftSignInButton />;
78
- }
79
- ```
80
-
81
- That's it! 🎉
82
-
83
- ## Components
84
-
85
- ### MsalAuthProvider
86
-
87
- The root provider that initializes MSAL.
88
-
89
- ```tsx
90
- <MsalAuthProvider
91
- clientId="YOUR_CLIENT_ID"
92
- tenantId="YOUR_TENANT_ID" // Optional
93
- scopes={['User.Read', 'Mail.Read']} // Optional
94
- enableLogging={true} // Optional
95
- >
96
- {children}
97
- </MsalAuthProvider>
98
- ```
99
-
100
- ### MicrosoftSignInButton
101
-
102
- Pre-styled sign-in button with Microsoft branding.
103
-
104
- ```tsx
105
- <MicrosoftSignInButton
106
- variant="dark" // or "light"
107
- size="medium" // "small", "medium", "large"
108
- useRedirect={false} // Use popup by default
109
- onSuccess={() => console.log('Signed in!')}
110
- />
111
- ```
112
-
113
- ### SignOutButton
114
-
115
- Pre-styled sign-out button matching the sign-in button style.
116
-
117
- ```tsx
118
- <SignOutButton
119
- variant="light"
120
- size="medium"
121
- onSuccess={() => console.log('Signed out!')}
122
- />
123
- ```
124
-
125
- ### AuthGuard
126
-
127
- Protect pages/components that require authentication.
128
-
129
- ```tsx
130
- <AuthGuard
131
- loadingComponent={<div>Loading...</div>}
132
- fallbackComponent={<div>Redirecting to login...</div>}
133
- >
134
- <ProtectedContent />
135
- </AuthGuard>
136
- ```
137
-
138
- ### UserAvatar
139
-
140
- Display user photo from MS Graph with fallback initials.
141
-
142
- ```tsx
143
- <UserAvatar
144
- size={48}
145
- showTooltip={true}
146
- fallbackImage="/default-avatar.png"
147
- />
148
- ```
149
-
150
- ### AuthStatus
151
-
152
- Show current authentication state.
153
-
154
- ```tsx
155
- <AuthStatus
156
- showDetails={true}
157
- renderAuthenticated={(username) => (
158
- <div>Logged in as {username}</div>
159
- )}
160
- />
161
- ```
162
-
163
- ### ErrorBoundary
164
-
165
- Catch and handle authentication errors gracefully.
166
-
167
- ```tsx
168
- <ErrorBoundary
169
- fallback={(error, reset) => (
170
- <div>
171
- <p>Error: {error.message}</p>
172
- <button onClick={reset}>Try Again</button>
173
- </div>
174
- )}
175
- >
176
- <App />
177
- </ErrorBoundary>
178
- ```
179
-
180
- ## Hooks
181
-
182
- ### useMsalAuth
183
-
184
- Main authentication hook with all auth operations.
185
-
186
- ```tsx
187
- const {
188
- account,
189
- isAuthenticated,
190
- inProgress,
191
- loginPopup,
192
- loginRedirect,
193
- logoutPopup,
194
- logoutRedirect,
195
- acquireToken,
196
- } = useMsalAuth();
197
-
198
- // Login
199
- await loginPopup(['User.Read']);
200
-
201
- // Get token
202
- const token = await acquireToken(['User.Read']);
203
-
204
- // Logout
205
- await logoutPopup();
206
- ```
207
-
208
- ### useGraphApi
209
-
210
- Pre-configured fetch wrapper for MS Graph API.
211
-
212
- ```tsx
213
- const graph = useGraphApi();
214
-
215
- // GET request
216
- const user = await graph.get('/me');
217
-
218
- // POST request
219
- const message = await graph.post('/me/messages', {
220
- subject: 'Hello',
221
- body: { content: 'World' }
222
- });
223
-
224
- // Custom request
225
- const data = await graph.request('/me/drive', {
226
- scopes: ['Files.Read'],
227
- version: 'beta'
228
- });
229
- ```
230
-
231
- ### useUserProfile
232
-
233
- Fetch and cache user profile from MS Graph.
234
-
235
- ```tsx
236
- const { profile, loading, error, refetch } = useUserProfile();
237
-
238
- if (loading) return <div>Loading...</div>;
239
- if (error) return <div>Error: {error.message}</div>;
240
-
241
- return (
242
- <div>
243
- <h1>{profile.displayName}</h1>
244
- <p>{profile.mail}</p>
245
- <p>{profile.jobTitle}</p>
246
- </div>
247
- );
248
- ```
249
-
250
- ### useRoles
251
-
252
- Access user's Azure AD roles and groups.
253
-
254
- ```tsx
255
- const { roles, groups, hasRole, hasAnyRole } = useRoles();
256
-
257
- if (hasRole('Admin')) {
258
- return <AdminPanel />;
259
- }
260
-
261
- if (hasAnyRole(['Editor', 'Contributor'])) {
262
- return <EditorPanel />;
263
- }
264
-
265
- return <ViewerPanel />;
266
- ```
267
-
268
- ## Utilities
269
-
270
- ### withAuth
271
-
272
- Higher-order component for protecting pages.
273
-
274
- ```tsx
275
- const ProtectedPage = withAuth(MyPage, {
276
- useRedirect: true,
277
- scopes: ['User.Read']
278
- });
279
-
280
- export default ProtectedPage;
281
- ```
282
-
283
- ### getServerSession
284
-
285
- Server-side session helper for App Router.
286
-
287
- **Important:** Import from `@chemmangat/msal-next/server` in Server Components only.
288
-
289
- ```tsx
290
- // app/dashboard/page.tsx
291
- import { getServerSession } from '@chemmangat/msal-next/server';
292
- import { redirect } from 'next/navigation';
293
-
294
- export default async function DashboardPage() {
295
- const session = await getServerSession();
296
-
297
- if (!session.isAuthenticated) {
298
- redirect('/login');
299
- }
300
-
301
- return <div>Welcome {session.username}</div>;
302
- }
303
- ```
304
-
305
- ### createAuthMiddleware
306
-
307
- Protect routes at the edge with middleware.
308
-
309
- ```tsx
310
- // middleware.ts
311
- import { createAuthMiddleware } from '@chemmangat/msal-next';
312
-
313
- export const middleware = createAuthMiddleware({
314
- protectedRoutes: ['/dashboard', '/profile', '/api/protected'],
315
- publicOnlyRoutes: ['/login'],
316
- loginPath: '/login',
317
- debug: true,
318
- });
319
-
320
- export const config = {
321
- matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
322
- };
323
- ```
324
-
325
- ### Retry Logic
326
-
327
- Built-in exponential backoff for token acquisition.
328
-
329
- ```tsx
330
- import { retryWithBackoff, createRetryWrapper } from '@chemmangat/msal-next';
331
-
332
- // Wrap any async function with retry logic
333
- const token = await retryWithBackoff(
334
- () => acquireToken(['User.Read']),
335
- {
336
- maxRetries: 3,
337
- initialDelay: 1000,
338
- backoffMultiplier: 2,
339
- debug: true
340
- }
341
- );
342
-
343
- // Create a reusable retry wrapper
344
- const acquireTokenWithRetry = createRetryWrapper(acquireToken, {
345
- maxRetries: 3
346
- });
347
- ```
348
-
349
- ### Debug Logger
350
-
351
- Comprehensive logging for troubleshooting.
352
-
353
- ```tsx
354
- import { getDebugLogger } from '@chemmangat/msal-next';
355
-
356
- const logger = getDebugLogger({
357
- enabled: true,
358
- level: 'debug',
359
- showTimestamp: true
360
- });
361
-
362
- logger.info('User logged in', { username: 'user@example.com' });
363
- logger.error('Authentication failed', { error });
364
- ```
365
-
366
- ## TypeScript Support
367
-
368
- ### Custom Token Claims
369
-
370
- Extend the `CustomTokenClaims` interface for type-safe custom claims.
371
-
372
- ```tsx
373
- import { CustomTokenClaims } from '@chemmangat/msal-next';
374
-
375
- interface MyCustomClaims extends CustomTokenClaims {
376
- roles: string[];
377
- department: string;
378
- employeeId: string;
379
- }
380
-
381
- const { account } = useMsalAuth();
382
- const claims = account?.idTokenClaims as MyCustomClaims;
383
-
384
- console.log(claims.roles); // Type-safe!
385
- console.log(claims.department); // Type-safe!
386
- ```
387
-
388
- ## Advanced Examples
389
-
390
- ### Multi-Tenant Support
391
-
392
- ```tsx
393
- <MsalAuthProvider
394
- clientId="YOUR_CLIENT_ID"
395
- authorityType="common" // Supports any Azure AD tenant
396
- >
397
- {children}
398
- </MsalAuthProvider>
399
- ```
400
-
401
- ### Custom Scopes
402
-
403
- ```tsx
404
- <MsalAuthProvider
405
- clientId="YOUR_CLIENT_ID"
406
- scopes={[
407
- 'User.Read',
408
- 'Mail.Read',
409
- 'Calendars.Read',
410
- 'Files.Read.All'
411
- ]}
412
- >
413
- {children}
414
- </MsalAuthProvider>
415
- ```
416
-
417
- ### Multiple Account Selection
418
-
419
- ```tsx
420
- const { accounts, loginPopup } = useMsalAuth();
421
-
422
- // Show account picker
423
- await loginPopup(scopes, {
424
- prompt: 'select_account'
425
- });
426
-
427
- // List all accounts
428
- accounts.map(account => (
429
- <div key={account.homeAccountId}>
430
- {account.username}
431
- </div>
432
- ));
433
- ```
434
-
435
- ### Server-Side Rendering
436
-
437
- ```tsx
438
- // app/profile/page.tsx
439
- import { getServerSession } from '@chemmangat/msal-next';
440
-
441
- export default async function ProfilePage() {
442
- const session = await getServerSession();
443
-
444
- return (
445
- <div>
446
- <h1>Profile</h1>
447
- {session.isAuthenticated ? (
448
- <p>Welcome {session.username}</p>
449
- ) : (
450
- <p>Please sign in</p>
451
- )}
452
- </div>
453
- );
454
- }
455
- ```
456
-
457
- ### Role-Based Access Control
458
-
459
- ```tsx
460
- 'use client';
461
-
462
- import { useRoles, AuthGuard } from '@chemmangat/msal-next';
463
-
464
- function AdminPanel() {
465
- const { hasRole, hasAnyRole, hasAllRoles } = useRoles();
466
-
467
- if (!hasRole('Admin')) {
468
- return <div>Access denied</div>;
469
- }
470
-
471
- return <div>Admin content</div>;
472
- }
473
-
474
- export default function AdminPage() {
475
- return (
476
- <AuthGuard>
477
- <AdminPanel />
478
- </AuthGuard>
479
- );
480
- }
481
- ```
482
-
483
- ## Configuration Options
484
-
485
- ### MsalAuthConfig
486
-
487
- | Option | Type | Default | Description |
488
- |--------|------|---------|-------------|
489
- | `clientId` | `string` | **Required** | Azure AD Application (client) ID |
490
- | `tenantId` | `string` | `undefined` | Azure AD Directory (tenant) ID |
491
- | `authorityType` | `'common' \| 'organizations' \| 'consumers' \| 'tenant'` | `'common'` | Authority type |
492
- | `redirectUri` | `string` | `window.location.origin` | Redirect URI after auth |
493
- | `scopes` | `string[]` | `['User.Read']` | Default scopes |
494
- | `cacheLocation` | `'sessionStorage' \| 'localStorage' \| 'memoryStorage'` | `'sessionStorage'` | Token cache location |
495
- | `enableLogging` | `boolean` | `false` | Enable debug logging |
496
-
497
- ## Troubleshooting
498
-
499
- ### Common Issues
500
-
501
- **Issue**: "No active account" error
502
- **Solution**: Ensure user is logged in before calling `acquireToken`
503
-
504
- **Issue**: Token acquisition fails
505
- **Solution**: Check that required scopes are granted in Azure AD
506
-
507
- **Issue**: SSR hydration mismatch
508
- **Solution**: Use `'use client'` directive for components using auth hooks
509
-
510
- **Issue**: Middleware not protecting routes
511
- **Solution**: Ensure session cookies are being set after login
512
-
513
- ### Debug Mode
514
-
515
- Enable debug logging to troubleshoot issues:
516
-
517
- ```tsx
518
- <MsalAuthProvider
519
- clientId="YOUR_CLIENT_ID"
520
- enableLogging={true}
521
- >
522
- {children}
523
- </MsalAuthProvider>
524
- ```
525
-
526
- ## Migration Guide
527
-
528
- ### From v1.x to v2.x
529
-
530
- v2.0 is backward compatible with v1.x. New features are additive:
531
-
532
- ```tsx
533
- // v1.x - Still works!
534
- import { MsalAuthProvider, useMsalAuth } from '@chemmangat/msal-next';
535
-
536
- // v2.x - New features
537
- import {
538
- AuthGuard,
539
- SignOutButton,
540
- UserAvatar,
541
- useGraphApi,
542
- useUserProfile,
543
- useRoles,
544
- withAuth,
545
- createAuthMiddleware,
546
- } from '@chemmangat/msal-next';
547
- ```
548
-
549
- ## Contributing
550
-
551
- Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
552
-
553
- ## License
554
-
555
- MIT ÂĐ [Chemmangat](https://github.com/chemmangat)
556
-
557
- ## Support
558
-
559
- - 📖 [Documentation](https://github.com/chemmangat/msal-next#readme)
560
- - 🐛 [Issue Tracker](https://github.com/chemmangat/msal-next/issues)
561
- - 💎 [Discussions](https://github.com/chemmangat/msal-next/discussions)
562
-
563
- ## Acknowledgments
564
-
565
- Built with:
566
- - [@azure/msal-browser](https://github.com/AzureAD/microsoft-authentication-library-for-js)
567
- - [@azure/msal-react](https://github.com/AzureAD/microsoft-authentication-library-for-js)
568
- - [Next.js](https://nextjs.org/)
1
+ # @chemmangat/msal-next
2
+
3
+ Production-grade MSAL authentication library for Next.js App Router with minimal boilerplate.
4
+
5
+ [![npm version](https://badge.fury.io/js/@chemmangat%2Fmsal-next.svg)](https://www.npmjs.com/package/@chemmangat/msal-next)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ âœĻ **Minimal Setup** - Just provide your `clientId` to get started
11
+ 🔐 **Production Ready** - Comprehensive error handling, retry logic, and SSR support
12
+ ðŸŽĻ **Beautiful Components** - Pre-styled Microsoft-branded UI components
13
+ 🊝 **Powerful Hooks** - Easy-to-use hooks for auth, Graph API, and user data
14
+ ðŸ›Ąïļ **Type Safe** - Full TypeScript support with generics for custom claims
15
+ ⚡ **Edge Compatible** - Middleware support for protecting routes at the edge
16
+ ðŸ“Ķ **Zero Config** - Sensible defaults with full customization options
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @chemmangat/msal-next @azure/msal-browser @azure/msal-react
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### 1. Wrap your app with MsalAuthProvider
27
+
28
+ ```tsx
29
+ // app/layout.tsx
30
+ import { MsalAuthProvider } from '@chemmangat/msal-next';
31
+
32
+ export default function RootLayout({ children }) {
33
+ return (
34
+ <html>
35
+ <body>
36
+ <MsalAuthProvider clientId="YOUR_CLIENT_ID">
37
+ {children}
38
+ </MsalAuthProvider>
39
+ </body>
40
+ </html>
41
+ );
42
+ }
43
+ ```
44
+
45
+ ### 2. Add sign-in button
46
+
47
+ ```tsx
48
+ // app/page.tsx
49
+ 'use client';
50
+
51
+ import { MicrosoftSignInButton, useMsalAuth } from '@chemmangat/msal-next';
52
+
53
+ export default function Home() {
54
+ const { isAuthenticated } = useMsalAuth();
55
+
56
+ if (isAuthenticated) {
57
+ return <div>Welcome! You're signed in.</div>;
58
+ }
59
+
60
+ return <MicrosoftSignInButton />;
61
+ }
62
+ ```
63
+
64
+ That's it! 🎉
65
+
66
+ ## Components
67
+
68
+ ### MsalAuthProvider
69
+
70
+ The root provider that initializes MSAL.
71
+
72
+ ```tsx
73
+ <MsalAuthProvider
74
+ clientId="YOUR_CLIENT_ID"
75
+ tenantId="YOUR_TENANT_ID" // Optional
76
+ scopes={['User.Read', 'Mail.Read']} // Optional
77
+ enableLogging={true} // Optional
78
+ >
79
+ {children}
80
+ </MsalAuthProvider>
81
+ ```
82
+
83
+ ### MicrosoftSignInButton
84
+
85
+ Pre-styled sign-in button with Microsoft branding.
86
+
87
+ ```tsx
88
+ <MicrosoftSignInButton
89
+ variant="dark" // or "light"
90
+ size="medium" // "small", "medium", "large"
91
+ useRedirect={false} // Use popup by default
92
+ onSuccess={() => console.log('Signed in!')}
93
+ />
94
+ ```
95
+
96
+ ### SignOutButton
97
+
98
+ Pre-styled sign-out button matching the sign-in button style.
99
+
100
+ ```tsx
101
+ <SignOutButton
102
+ variant="light"
103
+ size="medium"
104
+ onSuccess={() => console.log('Signed out!')}
105
+ />
106
+ ```
107
+
108
+ ### AuthGuard
109
+
110
+ Protect pages/components that require authentication.
111
+
112
+ ```tsx
113
+ <AuthGuard
114
+ loadingComponent={<div>Loading...</div>}
115
+ fallbackComponent={<div>Redirecting to login...</div>}
116
+ >
117
+ <ProtectedContent />
118
+ </AuthGuard>
119
+ ```
120
+
121
+ ### UserAvatar
122
+
123
+ Display user photo from MS Graph with fallback initials.
124
+
125
+ ```tsx
126
+ <UserAvatar
127
+ size={48}
128
+ showTooltip={true}
129
+ fallbackImage="/default-avatar.png"
130
+ />
131
+ ```
132
+
133
+ ### AuthStatus
134
+
135
+ Show current authentication state.
136
+
137
+ ```tsx
138
+ <AuthStatus
139
+ showDetails={true}
140
+ renderAuthenticated={(username) => (
141
+ <div>Logged in as {username}</div>
142
+ )}
143
+ />
144
+ ```
145
+
146
+ ### ErrorBoundary
147
+
148
+ Catch and handle authentication errors gracefully.
149
+
150
+ ```tsx
151
+ <ErrorBoundary
152
+ fallback={(error, reset) => (
153
+ <div>
154
+ <p>Error: {error.message}</p>
155
+ <button onClick={reset}>Try Again</button>
156
+ </div>
157
+ )}
158
+ >
159
+ <App />
160
+ </ErrorBoundary>
161
+ ```
162
+
163
+ ## Hooks
164
+
165
+ ### useMsalAuth
166
+
167
+ Main authentication hook with all auth operations.
168
+
169
+ ```tsx
170
+ const {
171
+ account,
172
+ isAuthenticated,
173
+ inProgress,
174
+ loginPopup,
175
+ loginRedirect,
176
+ logoutPopup,
177
+ logoutRedirect,
178
+ acquireToken,
179
+ } = useMsalAuth();
180
+
181
+ // Login
182
+ await loginPopup(['User.Read']);
183
+
184
+ // Get token
185
+ const token = await acquireToken(['User.Read']);
186
+
187
+ // Logout
188
+ await logoutPopup();
189
+ ```
190
+
191
+ ### useGraphApi
192
+
193
+ Pre-configured fetch wrapper for MS Graph API.
194
+
195
+ ```tsx
196
+ const graph = useGraphApi();
197
+
198
+ // GET request
199
+ const user = await graph.get('/me');
200
+
201
+ // POST request
202
+ const message = await graph.post('/me/messages', {
203
+ subject: 'Hello',
204
+ body: { content: 'World' }
205
+ });
206
+
207
+ // Custom request
208
+ const data = await graph.request('/me/drive', {
209
+ scopes: ['Files.Read'],
210
+ version: 'beta'
211
+ });
212
+ ```
213
+
214
+ ### useUserProfile
215
+
216
+ Fetch and cache user profile from MS Graph.
217
+
218
+ ```tsx
219
+ const { profile, loading, error, refetch } = useUserProfile();
220
+
221
+ if (loading) return <div>Loading...</div>;
222
+ if (error) return <div>Error: {error.message}</div>;
223
+
224
+ return (
225
+ <div>
226
+ <h1>{profile.displayName}</h1>
227
+ <p>{profile.mail}</p>
228
+ <p>{profile.jobTitle}</p>
229
+ </div>
230
+ );
231
+ ```
232
+
233
+ ### useRoles
234
+
235
+ Access user's Azure AD roles and groups.
236
+
237
+ ```tsx
238
+ const { roles, groups, hasRole, hasAnyRole } = useRoles();
239
+
240
+ if (hasRole('Admin')) {
241
+ return <AdminPanel />;
242
+ }
243
+
244
+ if (hasAnyRole(['Editor', 'Contributor'])) {
245
+ return <EditorPanel />;
246
+ }
247
+
248
+ return <ViewerPanel />;
249
+ ```
250
+
251
+ ## Utilities
252
+
253
+ ### withAuth
254
+
255
+ Higher-order component for protecting pages.
256
+
257
+ ```tsx
258
+ const ProtectedPage = withAuth(MyPage, {
259
+ useRedirect: true,
260
+ scopes: ['User.Read']
261
+ });
262
+
263
+ export default ProtectedPage;
264
+ ```
265
+
266
+ ### getServerSession
267
+
268
+ Server-side session helper for App Router.
269
+
270
+ **Important:** Import from `@chemmangat/msal-next/server` in Server Components only.
271
+
272
+ ```tsx
273
+ // app/dashboard/page.tsx
274
+ import { getServerSession } from '@chemmangat/msal-next/server';
275
+ import { redirect } from 'next/navigation';
276
+
277
+ export default async function DashboardPage() {
278
+ const session = await getServerSession();
279
+
280
+ if (!session.isAuthenticated) {
281
+ redirect('/login');
282
+ }
283
+
284
+ return <div>Welcome {session.username}</div>;
285
+ }
286
+ ```
287
+
288
+ ### createAuthMiddleware
289
+
290
+ Protect routes at the edge with middleware.
291
+
292
+ ```tsx
293
+ // middleware.ts
294
+ import { createAuthMiddleware } from '@chemmangat/msal-next';
295
+
296
+ export const middleware = createAuthMiddleware({
297
+ protectedRoutes: ['/dashboard', '/profile', '/api/protected'],
298
+ publicOnlyRoutes: ['/login'],
299
+ loginPath: '/login',
300
+ debug: true,
301
+ });
302
+
303
+ export const config = {
304
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
305
+ };
306
+ ```
307
+
308
+ ### Retry Logic
309
+
310
+ Built-in exponential backoff for token acquisition.
311
+
312
+ ```tsx
313
+ import { retryWithBackoff, createRetryWrapper } from '@chemmangat/msal-next';
314
+
315
+ // Wrap any async function with retry logic
316
+ const token = await retryWithBackoff(
317
+ () => acquireToken(['User.Read']),
318
+ {
319
+ maxRetries: 3,
320
+ initialDelay: 1000,
321
+ backoffMultiplier: 2,
322
+ debug: true
323
+ }
324
+ );
325
+
326
+ // Create a reusable retry wrapper
327
+ const acquireTokenWithRetry = createRetryWrapper(acquireToken, {
328
+ maxRetries: 3
329
+ });
330
+ ```
331
+
332
+ ### Debug Logger
333
+
334
+ Comprehensive logging for troubleshooting.
335
+
336
+ ```tsx
337
+ import { getDebugLogger } from '@chemmangat/msal-next';
338
+
339
+ const logger = getDebugLogger({
340
+ enabled: true,
341
+ level: 'debug',
342
+ showTimestamp: true
343
+ });
344
+
345
+ logger.info('User logged in', { username: 'user@example.com' });
346
+ logger.error('Authentication failed', { error });
347
+ ```
348
+
349
+ ## TypeScript Support
350
+
351
+ ### Custom Token Claims
352
+
353
+ Extend the `CustomTokenClaims` interface for type-safe custom claims.
354
+
355
+ ```tsx
356
+ import { CustomTokenClaims } from '@chemmangat/msal-next';
357
+
358
+ interface MyCustomClaims extends CustomTokenClaims {
359
+ roles: string[];
360
+ department: string;
361
+ employeeId: string;
362
+ }
363
+
364
+ const { account } = useMsalAuth();
365
+ const claims = account?.idTokenClaims as MyCustomClaims;
366
+
367
+ console.log(claims.roles); // Type-safe!
368
+ console.log(claims.department); // Type-safe!
369
+ ```
370
+
371
+ ## Advanced Examples
372
+
373
+ ### Multi-Tenant Support
374
+
375
+ ```tsx
376
+ <MsalAuthProvider
377
+ clientId="YOUR_CLIENT_ID"
378
+ authorityType="common" // Supports any Azure AD tenant
379
+ >
380
+ {children}
381
+ </MsalAuthProvider>
382
+ ```
383
+
384
+ ### Custom Scopes
385
+
386
+ ```tsx
387
+ <MsalAuthProvider
388
+ clientId="YOUR_CLIENT_ID"
389
+ scopes={[
390
+ 'User.Read',
391
+ 'Mail.Read',
392
+ 'Calendars.Read',
393
+ 'Files.Read.All'
394
+ ]}
395
+ >
396
+ {children}
397
+ </MsalAuthProvider>
398
+ ```
399
+
400
+ ### Multiple Account Selection
401
+
402
+ ```tsx
403
+ const { accounts, loginPopup } = useMsalAuth();
404
+
405
+ // Show account picker
406
+ await loginPopup(scopes, {
407
+ prompt: 'select_account'
408
+ });
409
+
410
+ // List all accounts
411
+ accounts.map(account => (
412
+ <div key={account.homeAccountId}>
413
+ {account.username}
414
+ </div>
415
+ ));
416
+ ```
417
+
418
+ ### Server-Side Rendering
419
+
420
+ ```tsx
421
+ // app/profile/page.tsx
422
+ import { getServerSession } from '@chemmangat/msal-next';
423
+
424
+ export default async function ProfilePage() {
425
+ const session = await getServerSession();
426
+
427
+ return (
428
+ <div>
429
+ <h1>Profile</h1>
430
+ {session.isAuthenticated ? (
431
+ <p>Welcome {session.username}</p>
432
+ ) : (
433
+ <p>Please sign in</p>
434
+ )}
435
+ </div>
436
+ );
437
+ }
438
+ ```
439
+
440
+ ### Role-Based Access Control
441
+
442
+ ```tsx
443
+ 'use client';
444
+
445
+ import { useRoles, AuthGuard } from '@chemmangat/msal-next';
446
+
447
+ function AdminPanel() {
448
+ const { hasRole, hasAnyRole, hasAllRoles } = useRoles();
449
+
450
+ if (!hasRole('Admin')) {
451
+ return <div>Access denied</div>;
452
+ }
453
+
454
+ return <div>Admin content</div>;
455
+ }
456
+
457
+ export default function AdminPage() {
458
+ return (
459
+ <AuthGuard>
460
+ <AdminPanel />
461
+ </AuthGuard>
462
+ );
463
+ }
464
+ ```
465
+
466
+ ## Configuration Options
467
+
468
+ ### MsalAuthConfig
469
+
470
+ | Option | Type | Default | Description |
471
+ |--------|------|---------|-------------|
472
+ | `clientId` | `string` | **Required** | Azure AD Application (client) ID |
473
+ | `tenantId` | `string` | `undefined` | Azure AD Directory (tenant) ID |
474
+ | `authorityType` | `'common' \| 'organizations' \| 'consumers' \| 'tenant'` | `'common'` | Authority type |
475
+ | `redirectUri` | `string` | `window.location.origin` | Redirect URI after auth |
476
+ | `scopes` | `string[]` | `['User.Read']` | Default scopes |
477
+ | `cacheLocation` | `'sessionStorage' \| 'localStorage' \| 'memoryStorage'` | `'sessionStorage'` | Token cache location |
478
+ | `enableLogging` | `boolean` | `false` | Enable debug logging |
479
+
480
+ ## Troubleshooting
481
+
482
+ ### Common Issues
483
+
484
+ **Issue**: "No active account" error
485
+ **Solution**: Ensure user is logged in before calling `acquireToken`
486
+
487
+ **Issue**: Token acquisition fails
488
+ **Solution**: Check that required scopes are granted in Azure AD
489
+
490
+ **Issue**: SSR hydration mismatch
491
+ **Solution**: Use `'use client'` directive for components using auth hooks
492
+
493
+ **Issue**: Middleware not protecting routes
494
+ **Solution**: Ensure session cookies are being set after login
495
+
496
+ ### Debug Mode
497
+
498
+ Enable debug logging to troubleshoot issues:
499
+
500
+ ```tsx
501
+ <MsalAuthProvider
502
+ clientId="YOUR_CLIENT_ID"
503
+ enableLogging={true}
504
+ >
505
+ {children}
506
+ </MsalAuthProvider>
507
+ ```
508
+
509
+ ## Migration Guide
510
+
511
+ ### From v1.x to v2.x
512
+
513
+ v2.0 is backward compatible with v1.x. New features are additive:
514
+
515
+ ```tsx
516
+ // v1.x - Still works!
517
+ import { MsalAuthProvider, useMsalAuth } from '@chemmangat/msal-next';
518
+
519
+ // v2.x - New features
520
+ import {
521
+ AuthGuard,
522
+ SignOutButton,
523
+ UserAvatar,
524
+ useGraphApi,
525
+ useUserProfile,
526
+ useRoles,
527
+ withAuth,
528
+ createAuthMiddleware,
529
+ } from '@chemmangat/msal-next';
530
+ ```
531
+
532
+ ## Contributing
533
+
534
+ Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
535
+
536
+ ## License
537
+
538
+ MIT ÂĐ [Chemmangat](https://github.com/chemmangat)
539
+
540
+ ## Support
541
+
542
+ - 📖 [Documentation](https://github.com/chemmangat/msal-next#readme)
543
+ - 🐛 [Issue Tracker](https://github.com/chemmangat/msal-next/issues)
544
+ - 💎 [Discussions](https://github.com/chemmangat/msal-next/discussions)
545
+
546
+ ## Acknowledgments
547
+
548
+ Built with:
549
+ - [@azure/msal-browser](https://github.com/AzureAD/microsoft-authentication-library-for-js)
550
+ - [@azure/msal-react](https://github.com/AzureAD/microsoft-authentication-library-for-js)
551
+ - [Next.js](https://nextjs.org/)