@chemmangat/msal-next 4.2.0 → 4.2.1
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 +137 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Production-grade MSAL authentication library for Next.js App Router with minimal
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](./SECURITY.md)
|
|
8
8
|
|
|
9
|
-
> **📦 Current Version: 4.
|
|
9
|
+
> **📦 Current Version: 4.2.1** - Production-ready with automatic token refresh and enhanced security
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -466,6 +466,7 @@ Pre-styled sign-in button with Microsoft branding.
|
|
|
466
466
|
variant="dark" // or "light"
|
|
467
467
|
size="medium" // "small", "medium", "large"
|
|
468
468
|
onSuccess={() => console.log('Signed in!')}
|
|
469
|
+
onError={(error) => console.error(error)}
|
|
469
470
|
/>
|
|
470
471
|
```
|
|
471
472
|
|
|
@@ -477,6 +478,7 @@ Pre-styled sign-out button.
|
|
|
477
478
|
variant="light"
|
|
478
479
|
size="medium"
|
|
479
480
|
onSuccess={() => console.log('Signed out!')}
|
|
481
|
+
onError={(error) => console.error(error)}
|
|
480
482
|
/>
|
|
481
483
|
```
|
|
482
484
|
|
|
@@ -510,13 +512,16 @@ Main authentication hook.
|
|
|
510
512
|
|
|
511
513
|
```tsx
|
|
512
514
|
const {
|
|
513
|
-
account,
|
|
514
|
-
accounts,
|
|
515
|
-
isAuthenticated,
|
|
516
|
-
inProgress,
|
|
517
|
-
loginRedirect,
|
|
518
|
-
logoutRedirect,
|
|
519
|
-
acquireToken,
|
|
515
|
+
account, // Current user account
|
|
516
|
+
accounts, // All cached accounts
|
|
517
|
+
isAuthenticated, // Boolean: is user signed in?
|
|
518
|
+
inProgress, // Boolean: is auth in progress?
|
|
519
|
+
loginRedirect, // Function: sign in (redirect flow)
|
|
520
|
+
logoutRedirect, // Function: sign out (redirect flow)
|
|
521
|
+
acquireToken, // Function: get access token (silent with redirect fallback)
|
|
522
|
+
acquireTokenSilent, // Function: get access token (silent only, no fallback)
|
|
523
|
+
acquireTokenRedirect, // Function: get access token via redirect
|
|
524
|
+
clearSession, // Function: clear MSAL session without Microsoft logout
|
|
520
525
|
} = useMsalAuth();
|
|
521
526
|
```
|
|
522
527
|
|
|
@@ -553,18 +558,30 @@ const message = await graph.post('/me/messages', {
|
|
|
553
558
|
subject: 'Hello',
|
|
554
559
|
body: { content: 'World' }
|
|
555
560
|
});
|
|
561
|
+
|
|
562
|
+
// PUT, PATCH, DELETE
|
|
563
|
+
await graph.put('/me/photo/$value', photoBlob);
|
|
564
|
+
await graph.patch('/me', { displayName: 'New Name' });
|
|
565
|
+
await graph.delete('/me/messages/{id}');
|
|
566
|
+
|
|
567
|
+
// Custom request with options
|
|
568
|
+
const data = await graph.request('/me', { version: 'beta', scopes: ['User.Read'] });
|
|
556
569
|
```
|
|
557
570
|
|
|
558
571
|
#### useRoles()
|
|
559
|
-
Access user's Azure AD roles.
|
|
572
|
+
Access user's Azure AD roles and groups.
|
|
560
573
|
|
|
561
574
|
```tsx
|
|
562
575
|
const {
|
|
563
576
|
roles, // Array of role names
|
|
564
577
|
groups, // Array of group IDs
|
|
578
|
+
loading, // Boolean: is loading?
|
|
579
|
+
error, // Error object if failed
|
|
565
580
|
hasRole, // Function: check single role
|
|
566
|
-
|
|
567
|
-
|
|
581
|
+
hasGroup, // Function: check single group by ID
|
|
582
|
+
hasAnyRole, // Function: check if user has any of the given roles
|
|
583
|
+
hasAllRoles, // Function: check if user has all of the given roles
|
|
584
|
+
refetch, // Function: refetch roles and groups
|
|
568
585
|
} = useRoles();
|
|
569
586
|
|
|
570
587
|
if (hasRole('Admin')) {
|
|
@@ -572,9 +589,109 @@ if (hasRole('Admin')) {
|
|
|
572
589
|
}
|
|
573
590
|
```
|
|
574
591
|
|
|
592
|
+
#### useTokenRefresh()
|
|
593
|
+
Monitor and control token refresh state.
|
|
594
|
+
|
|
595
|
+
```tsx
|
|
596
|
+
const {
|
|
597
|
+
expiresIn, // Seconds until token expires (null if unknown)
|
|
598
|
+
isExpiringSoon, // Boolean: token expiring within threshold
|
|
599
|
+
refresh, // Function: manually trigger token refresh
|
|
600
|
+
lastRefresh, // Date: when token was last refreshed
|
|
601
|
+
} = useTokenRefresh({
|
|
602
|
+
refreshBeforeExpiry: 300, // seconds before expiry to refresh
|
|
603
|
+
scopes: ['User.Read'],
|
|
604
|
+
onRefresh: (expiresIn) => console.log(`Refreshed, expires in ${expiresIn}s`),
|
|
605
|
+
onError: (error) => console.error(error),
|
|
606
|
+
});
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
#### useMultiAccount()
|
|
610
|
+
Manage multiple signed-in Microsoft accounts.
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
const {
|
|
614
|
+
accounts, // All signed-in accounts
|
|
615
|
+
activeAccount, // Currently active account
|
|
616
|
+
hasMultipleAccounts, // Boolean: more than one account signed in
|
|
617
|
+
accountCount, // Number of signed-in accounts
|
|
618
|
+
inProgress, // Boolean: interaction in progress
|
|
619
|
+
switchAccount, // Function: switch active account
|
|
620
|
+
addAccount, // Function: sign in with another account
|
|
621
|
+
removeAccount, // Function: remove account from cache
|
|
622
|
+
signOutAccount, // Function: sign out a specific account
|
|
623
|
+
signOutAll, // Function: sign out all accounts
|
|
624
|
+
getAccountByUsername, // Function: find account by username
|
|
625
|
+
getAccountById, // Function: find account by homeAccountId
|
|
626
|
+
isActiveAccount, // Function: check if account is active
|
|
627
|
+
} = useMultiAccount();
|
|
628
|
+
```
|
|
629
|
+
|
|
575
630
|
---
|
|
576
631
|
|
|
577
|
-
|
|
632
|
+
### Additional Components
|
|
633
|
+
|
|
634
|
+
#### AccountSwitcher
|
|
635
|
+
Pre-built UI for switching between multiple signed-in accounts.
|
|
636
|
+
|
|
637
|
+
```tsx
|
|
638
|
+
<AccountSwitcher
|
|
639
|
+
showAvatars={true}
|
|
640
|
+
maxAccounts={5}
|
|
641
|
+
variant="default" // "default", "compact", "minimal"
|
|
642
|
+
showAddButton={true}
|
|
643
|
+
showRemoveButton={true}
|
|
644
|
+
onSwitch={(account) => console.log('Switched to', account.name)}
|
|
645
|
+
onAdd={() => console.log('Adding account')}
|
|
646
|
+
onRemove={(account) => console.log('Removed', account.name)}
|
|
647
|
+
/>
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### AccountList
|
|
651
|
+
Display all signed-in accounts in a list.
|
|
652
|
+
|
|
653
|
+
```tsx
|
|
654
|
+
<AccountList
|
|
655
|
+
showAvatars={true}
|
|
656
|
+
showDetails={true}
|
|
657
|
+
showActiveIndicator={true}
|
|
658
|
+
clickToSwitch={true}
|
|
659
|
+
orientation="vertical" // or "horizontal"
|
|
660
|
+
onAccountClick={(account) => console.log('Clicked', account.name)}
|
|
661
|
+
/>
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
### Higher-Order Components
|
|
667
|
+
|
|
668
|
+
#### withAuth
|
|
669
|
+
Protect a component by wrapping it with `AuthGuard`.
|
|
670
|
+
|
|
671
|
+
```tsx
|
|
672
|
+
const ProtectedPage = withAuth(MyPage);
|
|
673
|
+
|
|
674
|
+
// With options
|
|
675
|
+
const ProtectedPage = withAuth(MyPage, {
|
|
676
|
+
loadingComponent: <Spinner />,
|
|
677
|
+
fallbackComponent: <div>Please sign in</div>,
|
|
678
|
+
scopes: ['User.Read'],
|
|
679
|
+
});
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
#### withPageAuth
|
|
683
|
+
Add page-level auth protection with role support.
|
|
684
|
+
|
|
685
|
+
```tsx
|
|
686
|
+
const ProtectedDashboard = withPageAuth(Dashboard, {
|
|
687
|
+
required: true,
|
|
688
|
+
roles: ['Admin', 'Editor'],
|
|
689
|
+
redirectTo: '/login',
|
|
690
|
+
});
|
|
691
|
+
export default ProtectedDashboard;
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
---
|
|
578
695
|
|
|
579
696
|
### Automatic Token Refresh (NEW in v4.1.0)
|
|
580
697
|
|
|
@@ -598,12 +715,13 @@ Prevent unexpected logouts by automatically refreshing tokens before they expire
|
|
|
598
715
|
import { useTokenRefresh } from '@chemmangat/msal-next';
|
|
599
716
|
|
|
600
717
|
export default function SessionWarning() {
|
|
601
|
-
const { expiresIn, isExpiringSoon } = useTokenRefresh();
|
|
718
|
+
const { expiresIn, isExpiringSoon, refresh, lastRefresh } = useTokenRefresh();
|
|
602
719
|
|
|
603
720
|
if (isExpiringSoon) {
|
|
604
721
|
return (
|
|
605
722
|
<div className="warning">
|
|
606
|
-
⚠️ Your session will expire in {Math.floor(expiresIn / 60)} minutes
|
|
723
|
+
⚠️ Your session will expire in {Math.floor((expiresIn ?? 0) / 60)} minutes
|
|
724
|
+
<button onClick={refresh}>Refresh now</button>
|
|
607
725
|
</div>
|
|
608
726
|
);
|
|
609
727
|
}
|
|
@@ -752,9 +870,14 @@ export default function ProfilePage() {
|
|
|
752
870
|
| `tenantId` | `string` | No | - | Azure AD Directory (tenant) ID (for single-tenant) |
|
|
753
871
|
| `authorityType` | `'common' \| 'organizations' \| 'consumers' \| 'tenant'` | No | `'common'` | Authority type |
|
|
754
872
|
| `redirectUri` | `string` | No | `window.location.origin` | Redirect URI after authentication |
|
|
873
|
+
| `postLogoutRedirectUri` | `string` | No | `redirectUri` | Redirect URI after logout |
|
|
755
874
|
| `scopes` | `string[]` | No | `['User.Read']` | Default scopes |
|
|
756
875
|
| `cacheLocation` | `'sessionStorage' \| 'localStorage' \| 'memoryStorage'` | No | `'sessionStorage'` | Token cache location |
|
|
757
876
|
| `enableLogging` | `boolean` | No | `false` | Enable debug logging |
|
|
877
|
+
| `autoRefreshToken` | `boolean` | No | `false` | Automatically refresh tokens before expiry |
|
|
878
|
+
| `refreshBeforeExpiry` | `number` | No | `300` | Seconds before expiry to refresh token |
|
|
879
|
+
| `allowedRedirectUris` | `string[]` | No | - | Whitelist of allowed redirect URIs |
|
|
880
|
+
| `protection` | `AuthProtectionConfig` | No | - | Zero-config protected routes configuration |
|
|
758
881
|
|
|
759
882
|
### Authority Types
|
|
760
883
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chemmangat/msal-next",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Production-ready Microsoft/Azure AD authentication for Next.js App Router. Zero-config setup, TypeScript-first, multi-account support, auto token refresh. The easiest way to add Microsoft login to your Next.js app.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|