@chemmangat/msal-next 3.1.6 → 3.1.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/CHANGELOG.md +33 -36
- package/README.md +7 -65
- package/dist/index.d.mts +6 -40
- package/dist/index.d.ts +6 -40
- package/dist/index.js +14 -120
- package/dist/index.mjs +15 -120
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,57 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [3.1.
|
|
5
|
+
## [3.1.6] - 2026-03-05
|
|
6
6
|
|
|
7
|
-
### 🔄 Breaking Change - Redirect Flow
|
|
7
|
+
### 🔄 Breaking Change - Redirect-Only Flow
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Removed all popup authentication support** - Package now only supports redirect flow for cleaner, simpler authentication.
|
|
10
10
|
|
|
11
11
|
**Why this change?**
|
|
12
|
-
- Popup flow
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
12
|
+
- Popup flow had persistent issues (full app loading in popup, logout popups, etc.)
|
|
13
|
+
- Redirect flow is simpler, more reliable, and works out of the box
|
|
14
|
+
- No need for blank.html or special Azure AD configuration
|
|
15
|
+
- Better user experience with full-page redirects
|
|
16
|
+
|
|
17
|
+
**What was removed:**
|
|
18
|
+
- `loginPopup()` method
|
|
19
|
+
- `logoutPopup()` method
|
|
20
|
+
- `acquireTokenPopup()` method
|
|
21
|
+
- `useRedirect` prop from MicrosoftSignInButton
|
|
22
|
+
- `useRedirect` prop from SignOutButton
|
|
23
|
+
- `popupRedirectUri` configuration option
|
|
24
|
+
- `getPopupRedirectUri()` utility
|
|
25
|
+
- All popup-related code and configuration
|
|
16
26
|
|
|
17
27
|
**Migration:**
|
|
18
28
|
|
|
19
|
-
If you were using the default popup behavior:
|
|
20
29
|
```tsx
|
|
21
|
-
// Before (v3.1.4 and earlier)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<MicrosoftSignInButton /> // Now redirects full page
|
|
30
|
+
// Before (v3.1.4 and earlier)
|
|
31
|
+
const { loginPopup, logoutPopup } = useMsalAuth();
|
|
32
|
+
await loginPopup();
|
|
33
|
+
await logoutPopup();
|
|
26
34
|
|
|
27
|
-
// To keep popup behavior:
|
|
28
35
|
<MicrosoftSignInButton useRedirect={false} />
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
**New Default Behavior:**
|
|
32
|
-
- Button redirects the entire browser window to Microsoft login
|
|
33
|
-
- After authentication, redirects back to your app
|
|
34
|
-
- No popup windows, no blank.html needed
|
|
35
|
-
- Works with your existing Azure AD redirect URI
|
|
36
|
+
<SignOutButton useRedirect={false} />
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
// After (v3.1.5) - redirect only
|
|
39
|
+
const { loginRedirect, logoutRedirect } = useMsalAuth();
|
|
40
|
+
await loginRedirect();
|
|
41
|
+
await logoutRedirect();
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**For Popup Flow (Optional):**
|
|
42
|
-
1. Create `public/blank.html`
|
|
43
|
-
2. Add `/blank.html` to Azure AD redirect URIs
|
|
44
|
-
3. Use:
|
|
45
|
-
```tsx
|
|
46
|
-
<MSALProvider popupRedirectUri="/blank.html">
|
|
47
|
-
<MicrosoftSignInButton useRedirect={false} />
|
|
48
|
-
</MSALProvider>
|
|
43
|
+
<MicrosoftSignInButton />
|
|
44
|
+
<SignOutButton />
|
|
49
45
|
```
|
|
50
46
|
|
|
51
47
|
**Benefits:**
|
|
52
|
-
- Simpler
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
48
|
+
- Simpler API - no popup vs redirect decisions
|
|
49
|
+
- No popup-related bugs or issues
|
|
50
|
+
- Works perfectly out of the box
|
|
51
|
+
- Cleaner codebase and smaller bundle size
|
|
52
|
+
- Better user experience
|
|
56
53
|
|
|
57
54
|
## [3.1.4] - 2026-03-05
|
|
58
55
|
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Production-grade MSAL authentication library for Next.js App Router with minimal
|
|
|
5
5
|
[](https://www.npmjs.com/package/@chemmangat/msal-next)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
> **📦 Current Version: 3.1.
|
|
8
|
+
> **📦 Current Version: 3.1.6** - Redirect-only authentication (popup support removed). [See changelog](./CHANGELOG.md)
|
|
9
9
|
|
|
10
10
|
> **⚠️ Important:** If you're on v3.0.6 or v3.0.7, please update immediately - those versions have a critical popup authentication bug.
|
|
11
11
|
|
|
@@ -134,59 +134,7 @@ export default function Home() {
|
|
|
134
134
|
|
|
135
135
|
That's it! 🎉
|
|
136
136
|
|
|
137
|
-
The button uses redirect flow
|
|
138
|
-
|
|
139
|
-
## Optional: Fix Popup Window Issue
|
|
140
|
-
|
|
141
|
-
**Important:** When using popup authentication, the popup window MUST navigate to your redirect URI to complete the OAuth flow. This is how OAuth works and cannot be avoided.
|
|
142
|
-
|
|
143
|
-
By default, this means your full Next.js app will briefly load in the popup before it closes. The package detects this and renders minimal content, but you may still see a flash of your app.
|
|
144
|
-
|
|
145
|
-
### Solution: Use a Blank Page (Recommended for Popup Flow)
|
|
146
|
-
|
|
147
|
-
For the cleanest popup experience, create a blank HTML page:
|
|
148
|
-
|
|
149
|
-
### 1. Create blank.html
|
|
150
|
-
|
|
151
|
-
Create `public/blank.html`:
|
|
152
|
-
|
|
153
|
-
```html
|
|
154
|
-
<!DOCTYPE html>
|
|
155
|
-
<html>
|
|
156
|
-
<head><title>Auth</title></head>
|
|
157
|
-
<body></body>
|
|
158
|
-
</html>
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### 2. Add to Azure AD
|
|
162
|
-
|
|
163
|
-
Add to Azure AD redirect URIs:
|
|
164
|
-
- `http://localhost:3000/blank.html`
|
|
165
|
-
- `https://yourdomain.com/blank.html`
|
|
166
|
-
|
|
167
|
-
### 3. Configure MSALProvider
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
<MSALProvider
|
|
171
|
-
clientId={process.env.NEXT_PUBLIC_AZURE_AD_CLIENT_ID!}
|
|
172
|
-
tenantId={process.env.NEXT_PUBLIC_AZURE_AD_TENANT_ID!}
|
|
173
|
-
popupRedirectUri="/blank.html" // Add this line
|
|
174
|
-
>
|
|
175
|
-
{children}
|
|
176
|
-
</MSALProvider>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
This ensures the popup shows only a blank page and closes immediately.
|
|
180
|
-
|
|
181
|
-
### Alternative: Use Redirect Flow
|
|
182
|
-
|
|
183
|
-
If you don't want to set up blank.html, use redirect flow instead:
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
<MicrosoftSignInButton useRedirect={true} />
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Redirect flow navigates the entire browser window (no popup), so there's no "app in popup" issue.
|
|
137
|
+
The button uses redirect flow (full page redirect to Microsoft login, then back to your app). Simple and clean.
|
|
190
138
|
|
|
191
139
|
## Components
|
|
192
140
|
|
|
@@ -240,18 +188,14 @@ export function MyProviders({ children }) {
|
|
|
240
188
|
|
|
241
189
|
### MicrosoftSignInButton
|
|
242
190
|
|
|
243
|
-
Pre-styled sign-in button with Microsoft branding. Uses redirect flow
|
|
191
|
+
Pre-styled sign-in button with Microsoft branding. Uses redirect flow (full page redirect to Microsoft login).
|
|
244
192
|
|
|
245
193
|
```tsx
|
|
246
194
|
<MicrosoftSignInButton
|
|
247
195
|
variant="dark" // or "light"
|
|
248
196
|
size="medium" // "small", "medium", "large"
|
|
249
|
-
useRedirect={true} // Default: true (full page redirect)
|
|
250
197
|
onSuccess={() => console.log('Signed in!')}
|
|
251
198
|
/>
|
|
252
|
-
|
|
253
|
-
// If you prefer popup (requires blank.html setup):
|
|
254
|
-
<MicrosoftSignInButton useRedirect={false} />
|
|
255
199
|
```
|
|
256
200
|
|
|
257
201
|
### SignOutButton
|
|
@@ -332,21 +276,19 @@ const {
|
|
|
332
276
|
account,
|
|
333
277
|
isAuthenticated,
|
|
334
278
|
inProgress,
|
|
335
|
-
loginPopup,
|
|
336
279
|
loginRedirect,
|
|
337
|
-
logoutPopup,
|
|
338
280
|
logoutRedirect,
|
|
339
281
|
acquireToken,
|
|
340
282
|
} = useMsalAuth();
|
|
341
283
|
|
|
342
|
-
// Login
|
|
343
|
-
await
|
|
284
|
+
// Login (redirects to Microsoft)
|
|
285
|
+
await loginRedirect(['User.Read']);
|
|
344
286
|
|
|
345
287
|
// Get token
|
|
346
288
|
const token = await acquireToken(['User.Read']);
|
|
347
289
|
|
|
348
|
-
// Logout
|
|
349
|
-
await
|
|
290
|
+
// Logout (redirects to Microsoft)
|
|
291
|
+
await logoutRedirect();
|
|
350
292
|
```
|
|
351
293
|
|
|
352
294
|
### useGraphApi
|
package/dist/index.d.mts
CHANGED
|
@@ -41,13 +41,6 @@ interface MsalAuthConfig {
|
|
|
41
41
|
* @default window.location.origin
|
|
42
42
|
*/
|
|
43
43
|
redirectUri?: string;
|
|
44
|
-
/**
|
|
45
|
-
* Redirect URI for popup authentication (optional)
|
|
46
|
-
* If not specified, uses the same redirectUri as redirect flow
|
|
47
|
-
* Only set this if you want a different URI for popup (e.g., /blank.html)
|
|
48
|
-
* @default redirectUri
|
|
49
|
-
*/
|
|
50
|
-
popupRedirectUri?: string;
|
|
51
44
|
/**
|
|
52
45
|
* Post logout redirect URI
|
|
53
46
|
* @default redirectUri
|
|
@@ -156,11 +149,6 @@ interface MicrosoftSignInButtonProps {
|
|
|
156
149
|
* @default 'medium'
|
|
157
150
|
*/
|
|
158
151
|
size?: 'small' | 'medium' | 'large';
|
|
159
|
-
/**
|
|
160
|
-
* Use redirect flow instead of popup
|
|
161
|
-
* @default true
|
|
162
|
-
*/
|
|
163
|
-
useRedirect?: boolean;
|
|
164
152
|
/**
|
|
165
153
|
* Scopes to request
|
|
166
154
|
*/
|
|
@@ -182,7 +170,7 @@ interface MicrosoftSignInButtonProps {
|
|
|
182
170
|
*/
|
|
183
171
|
onError?: (error: Error) => void;
|
|
184
172
|
}
|
|
185
|
-
declare function MicrosoftSignInButton({ text, variant, size,
|
|
173
|
+
declare function MicrosoftSignInButton({ text, variant, size, scopes, className, style, onSuccess, onError, }: MicrosoftSignInButtonProps): react_jsx_runtime.JSX.Element;
|
|
186
174
|
|
|
187
175
|
interface SignOutButtonProps {
|
|
188
176
|
/**
|
|
@@ -200,11 +188,6 @@ interface SignOutButtonProps {
|
|
|
200
188
|
* @default 'medium'
|
|
201
189
|
*/
|
|
202
190
|
size?: 'small' | 'medium' | 'large';
|
|
203
|
-
/**
|
|
204
|
-
* Use redirect flow instead of popup
|
|
205
|
-
* @default false
|
|
206
|
-
*/
|
|
207
|
-
useRedirect?: boolean;
|
|
208
191
|
/**
|
|
209
192
|
* Custom className
|
|
210
193
|
*/
|
|
@@ -224,13 +207,14 @@ interface SignOutButtonProps {
|
|
|
224
207
|
}
|
|
225
208
|
/**
|
|
226
209
|
* SignOutButton component with Microsoft branding
|
|
210
|
+
* Uses redirect flow (full page redirect)
|
|
227
211
|
*
|
|
228
212
|
* @example
|
|
229
213
|
* ```tsx
|
|
230
214
|
* <SignOutButton variant="light" />
|
|
231
215
|
* ```
|
|
232
216
|
*/
|
|
233
|
-
declare function SignOutButton({ text, variant, size,
|
|
217
|
+
declare function SignOutButton({ text, variant, size, className, style, onSuccess, onError, }: SignOutButtonProps): react_jsx_runtime.JSX.Element;
|
|
234
218
|
|
|
235
219
|
interface UserAvatarProps {
|
|
236
220
|
/**
|
|
@@ -316,11 +300,6 @@ interface AuthGuardProps {
|
|
|
316
300
|
* Component to show when not authenticated (before redirect)
|
|
317
301
|
*/
|
|
318
302
|
fallbackComponent?: ReactNode;
|
|
319
|
-
/**
|
|
320
|
-
* Use redirect flow instead of popup
|
|
321
|
-
* @default true
|
|
322
|
-
*/
|
|
323
|
-
useRedirect?: boolean;
|
|
324
303
|
/**
|
|
325
304
|
* Scopes to request during authentication
|
|
326
305
|
*/
|
|
@@ -340,7 +319,7 @@ interface AuthGuardProps {
|
|
|
340
319
|
* </AuthGuard>
|
|
341
320
|
* ```
|
|
342
321
|
*/
|
|
343
|
-
declare function AuthGuard({ children, loadingComponent, fallbackComponent,
|
|
322
|
+
declare function AuthGuard({ children, loadingComponent, fallbackComponent, scopes, onAuthRequired, }: AuthGuardProps): react_jsx_runtime.JSX.Element;
|
|
344
323
|
|
|
345
324
|
interface ErrorBoundaryProps {
|
|
346
325
|
/**
|
|
@@ -402,34 +381,22 @@ interface UseMsalAuthReturn {
|
|
|
402
381
|
* Whether MSAL is currently performing an interaction
|
|
403
382
|
*/
|
|
404
383
|
inProgress: boolean;
|
|
405
|
-
/**
|
|
406
|
-
* Login using popup
|
|
407
|
-
*/
|
|
408
|
-
loginPopup: (scopes?: string[]) => Promise<void>;
|
|
409
384
|
/**
|
|
410
385
|
* Login using redirect
|
|
411
386
|
*/
|
|
412
387
|
loginRedirect: (scopes?: string[]) => Promise<void>;
|
|
413
|
-
/**
|
|
414
|
-
* Logout using popup
|
|
415
|
-
*/
|
|
416
|
-
logoutPopup: () => Promise<void>;
|
|
417
388
|
/**
|
|
418
389
|
* Logout using redirect
|
|
419
390
|
*/
|
|
420
391
|
logoutRedirect: () => Promise<void>;
|
|
421
392
|
/**
|
|
422
|
-
* Acquire access token silently
|
|
393
|
+
* Acquire access token silently
|
|
423
394
|
*/
|
|
424
395
|
acquireToken: (scopes: string[]) => Promise<string>;
|
|
425
396
|
/**
|
|
426
397
|
* Acquire access token silently only (no fallback)
|
|
427
398
|
*/
|
|
428
399
|
acquireTokenSilent: (scopes: string[]) => Promise<string>;
|
|
429
|
-
/**
|
|
430
|
-
* Acquire access token using popup
|
|
431
|
-
*/
|
|
432
|
-
acquireTokenPopup: (scopes: string[]) => Promise<string>;
|
|
433
400
|
/**
|
|
434
401
|
* Acquire access token using redirect
|
|
435
402
|
*/
|
|
@@ -591,7 +558,6 @@ interface UseRolesReturn {
|
|
|
591
558
|
*/
|
|
592
559
|
declare function useRoles(): UseRolesReturn;
|
|
593
560
|
|
|
594
|
-
declare function getPopupRedirectUri(): string | undefined;
|
|
595
561
|
declare function createMsalConfig(config: MsalAuthConfig): Configuration;
|
|
596
562
|
|
|
597
563
|
interface WithAuthOptions extends Omit<AuthGuardProps, 'children'> {
|
|
@@ -939,4 +905,4 @@ interface ServerSession {
|
|
|
939
905
|
accessToken?: string;
|
|
940
906
|
}
|
|
941
907
|
|
|
942
|
-
export { AuthGuard, type AuthGuardProps, type AuthMiddlewareConfig, AuthStatus, type AuthStatusProps, type CustomTokenClaims, type DebugLoggerConfig, ErrorBoundary, type ErrorBoundaryProps, type GraphApiOptions, MSALProvider, MicrosoftSignInButton, type MicrosoftSignInButtonProps, type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, type RetryConfig, type ServerSession, SignOutButton, type SignOutButtonProps, type UseGraphApiReturn, type UseMsalAuthReturn, type UseRolesReturn, type UseUserProfileReturn, UserAvatar, type UserAvatarProps, type UserProfile, type ValidatedAccountData, type WithAuthOptions, createAuthMiddleware, createMsalConfig, createRetryWrapper, createScopedLogger, getDebugLogger, getMsalInstance,
|
|
908
|
+
export { AuthGuard, type AuthGuardProps, type AuthMiddlewareConfig, AuthStatus, type AuthStatusProps, type CustomTokenClaims, type DebugLoggerConfig, ErrorBoundary, type ErrorBoundaryProps, type GraphApiOptions, MSALProvider, MicrosoftSignInButton, type MicrosoftSignInButtonProps, type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, type RetryConfig, type ServerSession, SignOutButton, type SignOutButtonProps, type UseGraphApiReturn, type UseMsalAuthReturn, type UseRolesReturn, type UseUserProfileReturn, UserAvatar, type UserAvatarProps, type UserProfile, type ValidatedAccountData, type WithAuthOptions, createAuthMiddleware, createMsalConfig, createRetryWrapper, createScopedLogger, getDebugLogger, getMsalInstance, isValidAccountData, isValidRedirectUri, isValidScope, retryWithBackoff, safeJsonParse, sanitizeError, useGraphApi, useMsalAuth, useRoles, useUserProfile, validateScopes, withAuth };
|
package/dist/index.d.ts
CHANGED
|
@@ -41,13 +41,6 @@ interface MsalAuthConfig {
|
|
|
41
41
|
* @default window.location.origin
|
|
42
42
|
*/
|
|
43
43
|
redirectUri?: string;
|
|
44
|
-
/**
|
|
45
|
-
* Redirect URI for popup authentication (optional)
|
|
46
|
-
* If not specified, uses the same redirectUri as redirect flow
|
|
47
|
-
* Only set this if you want a different URI for popup (e.g., /blank.html)
|
|
48
|
-
* @default redirectUri
|
|
49
|
-
*/
|
|
50
|
-
popupRedirectUri?: string;
|
|
51
44
|
/**
|
|
52
45
|
* Post logout redirect URI
|
|
53
46
|
* @default redirectUri
|
|
@@ -156,11 +149,6 @@ interface MicrosoftSignInButtonProps {
|
|
|
156
149
|
* @default 'medium'
|
|
157
150
|
*/
|
|
158
151
|
size?: 'small' | 'medium' | 'large';
|
|
159
|
-
/**
|
|
160
|
-
* Use redirect flow instead of popup
|
|
161
|
-
* @default true
|
|
162
|
-
*/
|
|
163
|
-
useRedirect?: boolean;
|
|
164
152
|
/**
|
|
165
153
|
* Scopes to request
|
|
166
154
|
*/
|
|
@@ -182,7 +170,7 @@ interface MicrosoftSignInButtonProps {
|
|
|
182
170
|
*/
|
|
183
171
|
onError?: (error: Error) => void;
|
|
184
172
|
}
|
|
185
|
-
declare function MicrosoftSignInButton({ text, variant, size,
|
|
173
|
+
declare function MicrosoftSignInButton({ text, variant, size, scopes, className, style, onSuccess, onError, }: MicrosoftSignInButtonProps): react_jsx_runtime.JSX.Element;
|
|
186
174
|
|
|
187
175
|
interface SignOutButtonProps {
|
|
188
176
|
/**
|
|
@@ -200,11 +188,6 @@ interface SignOutButtonProps {
|
|
|
200
188
|
* @default 'medium'
|
|
201
189
|
*/
|
|
202
190
|
size?: 'small' | 'medium' | 'large';
|
|
203
|
-
/**
|
|
204
|
-
* Use redirect flow instead of popup
|
|
205
|
-
* @default false
|
|
206
|
-
*/
|
|
207
|
-
useRedirect?: boolean;
|
|
208
191
|
/**
|
|
209
192
|
* Custom className
|
|
210
193
|
*/
|
|
@@ -224,13 +207,14 @@ interface SignOutButtonProps {
|
|
|
224
207
|
}
|
|
225
208
|
/**
|
|
226
209
|
* SignOutButton component with Microsoft branding
|
|
210
|
+
* Uses redirect flow (full page redirect)
|
|
227
211
|
*
|
|
228
212
|
* @example
|
|
229
213
|
* ```tsx
|
|
230
214
|
* <SignOutButton variant="light" />
|
|
231
215
|
* ```
|
|
232
216
|
*/
|
|
233
|
-
declare function SignOutButton({ text, variant, size,
|
|
217
|
+
declare function SignOutButton({ text, variant, size, className, style, onSuccess, onError, }: SignOutButtonProps): react_jsx_runtime.JSX.Element;
|
|
234
218
|
|
|
235
219
|
interface UserAvatarProps {
|
|
236
220
|
/**
|
|
@@ -316,11 +300,6 @@ interface AuthGuardProps {
|
|
|
316
300
|
* Component to show when not authenticated (before redirect)
|
|
317
301
|
*/
|
|
318
302
|
fallbackComponent?: ReactNode;
|
|
319
|
-
/**
|
|
320
|
-
* Use redirect flow instead of popup
|
|
321
|
-
* @default true
|
|
322
|
-
*/
|
|
323
|
-
useRedirect?: boolean;
|
|
324
303
|
/**
|
|
325
304
|
* Scopes to request during authentication
|
|
326
305
|
*/
|
|
@@ -340,7 +319,7 @@ interface AuthGuardProps {
|
|
|
340
319
|
* </AuthGuard>
|
|
341
320
|
* ```
|
|
342
321
|
*/
|
|
343
|
-
declare function AuthGuard({ children, loadingComponent, fallbackComponent,
|
|
322
|
+
declare function AuthGuard({ children, loadingComponent, fallbackComponent, scopes, onAuthRequired, }: AuthGuardProps): react_jsx_runtime.JSX.Element;
|
|
344
323
|
|
|
345
324
|
interface ErrorBoundaryProps {
|
|
346
325
|
/**
|
|
@@ -402,34 +381,22 @@ interface UseMsalAuthReturn {
|
|
|
402
381
|
* Whether MSAL is currently performing an interaction
|
|
403
382
|
*/
|
|
404
383
|
inProgress: boolean;
|
|
405
|
-
/**
|
|
406
|
-
* Login using popup
|
|
407
|
-
*/
|
|
408
|
-
loginPopup: (scopes?: string[]) => Promise<void>;
|
|
409
384
|
/**
|
|
410
385
|
* Login using redirect
|
|
411
386
|
*/
|
|
412
387
|
loginRedirect: (scopes?: string[]) => Promise<void>;
|
|
413
|
-
/**
|
|
414
|
-
* Logout using popup
|
|
415
|
-
*/
|
|
416
|
-
logoutPopup: () => Promise<void>;
|
|
417
388
|
/**
|
|
418
389
|
* Logout using redirect
|
|
419
390
|
*/
|
|
420
391
|
logoutRedirect: () => Promise<void>;
|
|
421
392
|
/**
|
|
422
|
-
* Acquire access token silently
|
|
393
|
+
* Acquire access token silently
|
|
423
394
|
*/
|
|
424
395
|
acquireToken: (scopes: string[]) => Promise<string>;
|
|
425
396
|
/**
|
|
426
397
|
* Acquire access token silently only (no fallback)
|
|
427
398
|
*/
|
|
428
399
|
acquireTokenSilent: (scopes: string[]) => Promise<string>;
|
|
429
|
-
/**
|
|
430
|
-
* Acquire access token using popup
|
|
431
|
-
*/
|
|
432
|
-
acquireTokenPopup: (scopes: string[]) => Promise<string>;
|
|
433
400
|
/**
|
|
434
401
|
* Acquire access token using redirect
|
|
435
402
|
*/
|
|
@@ -591,7 +558,6 @@ interface UseRolesReturn {
|
|
|
591
558
|
*/
|
|
592
559
|
declare function useRoles(): UseRolesReturn;
|
|
593
560
|
|
|
594
|
-
declare function getPopupRedirectUri(): string | undefined;
|
|
595
561
|
declare function createMsalConfig(config: MsalAuthConfig): Configuration;
|
|
596
562
|
|
|
597
563
|
interface WithAuthOptions extends Omit<AuthGuardProps, 'children'> {
|
|
@@ -939,4 +905,4 @@ interface ServerSession {
|
|
|
939
905
|
accessToken?: string;
|
|
940
906
|
}
|
|
941
907
|
|
|
942
|
-
export { AuthGuard, type AuthGuardProps, type AuthMiddlewareConfig, AuthStatus, type AuthStatusProps, type CustomTokenClaims, type DebugLoggerConfig, ErrorBoundary, type ErrorBoundaryProps, type GraphApiOptions, MSALProvider, MicrosoftSignInButton, type MicrosoftSignInButtonProps, type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, type RetryConfig, type ServerSession, SignOutButton, type SignOutButtonProps, type UseGraphApiReturn, type UseMsalAuthReturn, type UseRolesReturn, type UseUserProfileReturn, UserAvatar, type UserAvatarProps, type UserProfile, type ValidatedAccountData, type WithAuthOptions, createAuthMiddleware, createMsalConfig, createRetryWrapper, createScopedLogger, getDebugLogger, getMsalInstance,
|
|
908
|
+
export { AuthGuard, type AuthGuardProps, type AuthMiddlewareConfig, AuthStatus, type AuthStatusProps, type CustomTokenClaims, type DebugLoggerConfig, ErrorBoundary, type ErrorBoundaryProps, type GraphApiOptions, MSALProvider, MicrosoftSignInButton, type MicrosoftSignInButtonProps, type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, type RetryConfig, type ServerSession, SignOutButton, type SignOutButtonProps, type UseGraphApiReturn, type UseMsalAuthReturn, type UseRolesReturn, type UseUserProfileReturn, UserAvatar, type UserAvatarProps, type UserProfile, type ValidatedAccountData, type WithAuthOptions, createAuthMiddleware, createMsalConfig, createRetryWrapper, createScopedLogger, getDebugLogger, getMsalInstance, isValidAccountData, isValidRedirectUri, isValidScope, retryWithBackoff, safeJsonParse, sanitizeError, useGraphApi, useMsalAuth, useRoles, useUserProfile, validateScopes, withAuth };
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,6 @@ __export(client_exports, {
|
|
|
35
35
|
createScopedLogger: () => createScopedLogger,
|
|
36
36
|
getDebugLogger: () => getDebugLogger,
|
|
37
37
|
getMsalInstance: () => getMsalInstance,
|
|
38
|
-
getPopupRedirectUri: () => getPopupRedirectUri,
|
|
39
38
|
isValidAccountData: () => isValidAccountData,
|
|
40
39
|
isValidRedirectUri: () => isValidRedirectUri,
|
|
41
40
|
isValidScope: () => isValidScope,
|
|
@@ -106,10 +105,6 @@ function validateScopes(scopes) {
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
// src/utils/createMsalConfig.ts
|
|
109
|
-
var storedPopupRedirectUri;
|
|
110
|
-
function getPopupRedirectUri() {
|
|
111
|
-
return storedPopupRedirectUri;
|
|
112
|
-
}
|
|
113
108
|
function createMsalConfig(config) {
|
|
114
109
|
if (config.msalConfig) {
|
|
115
110
|
return config.msalConfig;
|
|
@@ -119,7 +114,6 @@ function createMsalConfig(config) {
|
|
|
119
114
|
tenantId,
|
|
120
115
|
authorityType = "common",
|
|
121
116
|
redirectUri,
|
|
122
|
-
popupRedirectUri,
|
|
123
117
|
postLogoutRedirectUri,
|
|
124
118
|
cacheLocation = "sessionStorage",
|
|
125
119
|
storeAuthStateInCookie = false,
|
|
@@ -142,8 +136,6 @@ function createMsalConfig(config) {
|
|
|
142
136
|
};
|
|
143
137
|
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
144
138
|
const finalRedirectUri = redirectUri || defaultRedirectUri;
|
|
145
|
-
const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
|
|
146
|
-
storedPopupRedirectUri = finalPopupRedirectUri;
|
|
147
139
|
if (allowedRedirectUris && allowedRedirectUris.length > 0) {
|
|
148
140
|
if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
|
|
149
141
|
throw new Error(
|
|
@@ -170,10 +162,6 @@ function createMsalConfig(config) {
|
|
|
170
162
|
storeAuthStateInCookie
|
|
171
163
|
},
|
|
172
164
|
system: {
|
|
173
|
-
windowHashTimeout: 6e4,
|
|
174
|
-
// Increase timeout for popup
|
|
175
|
-
iframeHashTimeout: 6e3,
|
|
176
|
-
loadFrameTimeout: 0,
|
|
177
165
|
loggerOptions: {
|
|
178
166
|
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
179
167
|
if (containsPii || !enableLogging) return;
|
|
@@ -208,17 +196,10 @@ function getMsalInstance() {
|
|
|
208
196
|
function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
|
|
209
197
|
const [msalInstance, setMsalInstance] = (0, import_react.useState)(null);
|
|
210
198
|
const instanceRef = (0, import_react.useRef)(null);
|
|
211
|
-
const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
|
|
212
199
|
(0, import_react.useEffect)(() => {
|
|
213
200
|
if (typeof window === "undefined") {
|
|
214
201
|
return;
|
|
215
202
|
}
|
|
216
|
-
if (isInPopup) {
|
|
217
|
-
if (config.enableLogging) {
|
|
218
|
-
console.log("[MSAL] Detected popup window - minimal initialization");
|
|
219
|
-
}
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
203
|
if (instanceRef.current) {
|
|
223
204
|
return;
|
|
224
205
|
}
|
|
@@ -227,17 +208,16 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
227
208
|
const msalConfig = createMsalConfig(config);
|
|
228
209
|
const instance = new import_msal_browser2.PublicClientApplication(msalConfig);
|
|
229
210
|
await instance.initialize();
|
|
230
|
-
const isInPopup2 = window.opener && window.opener !== window;
|
|
231
211
|
try {
|
|
232
212
|
const response = await instance.handleRedirectPromise();
|
|
233
213
|
if (response) {
|
|
234
214
|
if (config.enableLogging) {
|
|
235
|
-
console.log("[MSAL] Redirect authentication successful"
|
|
215
|
+
console.log("[MSAL] Redirect authentication successful");
|
|
236
216
|
}
|
|
237
217
|
if (response.account) {
|
|
238
218
|
instance.setActiveAccount(response.account);
|
|
239
219
|
}
|
|
240
|
-
if (
|
|
220
|
+
if (window.location.hash) {
|
|
241
221
|
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
242
222
|
}
|
|
243
223
|
}
|
|
@@ -253,7 +233,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
253
233
|
} else {
|
|
254
234
|
console.error("[MSAL] Redirect handling error:", redirectError);
|
|
255
235
|
}
|
|
256
|
-
if (
|
|
236
|
+
if (window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
|
|
257
237
|
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
258
238
|
}
|
|
259
239
|
}
|
|
@@ -309,9 +289,6 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
309
289
|
if (typeof window === "undefined") {
|
|
310
290
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
|
|
311
291
|
}
|
|
312
|
-
if (isInPopup) {
|
|
313
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "none" }, children: "Completing authentication..." });
|
|
314
|
-
}
|
|
315
292
|
if (!msalInstance) {
|
|
316
293
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
|
|
317
294
|
}
|
|
@@ -332,37 +309,7 @@ var pendingTokenRequests = /* @__PURE__ */ new Map();
|
|
|
332
309
|
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
333
310
|
const { instance, accounts, inProgress } = (0, import_msal_react2.useMsal)();
|
|
334
311
|
const account = (0, import_msal_react2.useAccount)(accounts[0] || null);
|
|
335
|
-
const popupInProgressRef = (0, import_react2.useRef)(false);
|
|
336
312
|
const isAuthenticated = (0, import_react2.useMemo)(() => accounts.length > 0, [accounts]);
|
|
337
|
-
const loginPopup = (0, import_react2.useCallback)(
|
|
338
|
-
async (scopes = defaultScopes) => {
|
|
339
|
-
if (inProgress !== import_msal_browser3.InteractionStatus.None) {
|
|
340
|
-
console.warn("[MSAL] Interaction already in progress");
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
try {
|
|
344
|
-
const popupRedirectUri = getPopupRedirectUri();
|
|
345
|
-
const request = {
|
|
346
|
-
scopes,
|
|
347
|
-
prompt: "select_account",
|
|
348
|
-
// Use popup-specific redirect URI (defaults to /blank.html)
|
|
349
|
-
redirectUri: popupRedirectUri
|
|
350
|
-
};
|
|
351
|
-
const response = await instance.loginPopup(request);
|
|
352
|
-
if (response?.account) {
|
|
353
|
-
instance.setActiveAccount(response.account);
|
|
354
|
-
}
|
|
355
|
-
} catch (error) {
|
|
356
|
-
if (error?.errorCode === "user_cancelled") {
|
|
357
|
-
console.log("[MSAL] User cancelled login");
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
console.error("[MSAL] Login popup failed:", error);
|
|
361
|
-
throw error;
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
[instance, defaultScopes, inProgress]
|
|
365
|
-
);
|
|
366
313
|
const loginRedirect = (0, import_react2.useCallback)(
|
|
367
314
|
async (scopes = defaultScopes) => {
|
|
368
315
|
if (inProgress !== import_msal_browser3.InteractionStatus.None) {
|
|
@@ -386,16 +333,6 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
386
333
|
},
|
|
387
334
|
[instance, defaultScopes, inProgress]
|
|
388
335
|
);
|
|
389
|
-
const logoutPopup = (0, import_react2.useCallback)(async () => {
|
|
390
|
-
try {
|
|
391
|
-
await instance.logoutPopup({
|
|
392
|
-
account: account || void 0
|
|
393
|
-
});
|
|
394
|
-
} catch (error) {
|
|
395
|
-
console.error("[MSAL] Logout popup failed:", error);
|
|
396
|
-
throw error;
|
|
397
|
-
}
|
|
398
|
-
}, [instance, account]);
|
|
399
336
|
const logoutRedirect = (0, import_react2.useCallback)(async () => {
|
|
400
337
|
try {
|
|
401
338
|
await instance.logoutRedirect({
|
|
@@ -426,31 +363,6 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
426
363
|
},
|
|
427
364
|
[instance, account, defaultScopes]
|
|
428
365
|
);
|
|
429
|
-
const acquireTokenPopup = (0, import_react2.useCallback)(
|
|
430
|
-
async (scopes = defaultScopes) => {
|
|
431
|
-
if (!account) {
|
|
432
|
-
throw new Error("[MSAL] No active account. Please login first.");
|
|
433
|
-
}
|
|
434
|
-
if (popupInProgressRef.current) {
|
|
435
|
-
throw new Error("[MSAL] Popup already in progress. Please wait.");
|
|
436
|
-
}
|
|
437
|
-
try {
|
|
438
|
-
popupInProgressRef.current = true;
|
|
439
|
-
const request = {
|
|
440
|
-
scopes,
|
|
441
|
-
account
|
|
442
|
-
};
|
|
443
|
-
const response = await instance.acquireTokenPopup(request);
|
|
444
|
-
return response.accessToken;
|
|
445
|
-
} catch (error) {
|
|
446
|
-
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
447
|
-
throw error;
|
|
448
|
-
} finally {
|
|
449
|
-
popupInProgressRef.current = false;
|
|
450
|
-
}
|
|
451
|
-
},
|
|
452
|
-
[instance, account, defaultScopes]
|
|
453
|
-
);
|
|
454
366
|
const acquireTokenRedirect = (0, import_react2.useCallback)(
|
|
455
367
|
async (scopes = defaultScopes) => {
|
|
456
368
|
if (!account) {
|
|
@@ -480,8 +392,9 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
480
392
|
try {
|
|
481
393
|
return await acquireTokenSilent(scopes);
|
|
482
394
|
} catch (error) {
|
|
483
|
-
console.warn("[MSAL] Silent token acquisition failed, falling back to
|
|
484
|
-
|
|
395
|
+
console.warn("[MSAL] Silent token acquisition failed, falling back to redirect");
|
|
396
|
+
await acquireTokenRedirect(scopes);
|
|
397
|
+
throw new Error("[MSAL] Redirecting for token acquisition");
|
|
485
398
|
} finally {
|
|
486
399
|
pendingTokenRequests.delete(requestKey);
|
|
487
400
|
}
|
|
@@ -489,7 +402,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
489
402
|
pendingTokenRequests.set(requestKey, tokenRequest);
|
|
490
403
|
return tokenRequest;
|
|
491
404
|
},
|
|
492
|
-
[acquireTokenSilent,
|
|
405
|
+
[acquireTokenSilent, acquireTokenRedirect, defaultScopes, account]
|
|
493
406
|
);
|
|
494
407
|
const clearSession = (0, import_react2.useCallback)(async () => {
|
|
495
408
|
instance.setActiveAccount(null);
|
|
@@ -500,13 +413,10 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
500
413
|
accounts,
|
|
501
414
|
isAuthenticated,
|
|
502
415
|
inProgress: inProgress !== import_msal_browser3.InteractionStatus.None,
|
|
503
|
-
loginPopup,
|
|
504
416
|
loginRedirect,
|
|
505
|
-
logoutPopup,
|
|
506
417
|
logoutRedirect,
|
|
507
418
|
acquireToken,
|
|
508
419
|
acquireTokenSilent,
|
|
509
|
-
acquireTokenPopup,
|
|
510
420
|
acquireTokenRedirect,
|
|
511
421
|
clearSession
|
|
512
422
|
};
|
|
@@ -519,23 +429,18 @@ function MicrosoftSignInButton({
|
|
|
519
429
|
text = "Sign in with Microsoft",
|
|
520
430
|
variant = "dark",
|
|
521
431
|
size = "medium",
|
|
522
|
-
useRedirect = true,
|
|
523
432
|
scopes,
|
|
524
433
|
className = "",
|
|
525
434
|
style,
|
|
526
435
|
onSuccess,
|
|
527
436
|
onError
|
|
528
437
|
}) {
|
|
529
|
-
const {
|
|
438
|
+
const { loginRedirect, inProgress } = useMsalAuth();
|
|
530
439
|
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
531
440
|
const handleClick = async () => {
|
|
532
441
|
setIsLoading(true);
|
|
533
442
|
try {
|
|
534
|
-
|
|
535
|
-
await loginRedirect(scopes);
|
|
536
|
-
} else {
|
|
537
|
-
await loginPopup(scopes);
|
|
538
|
-
}
|
|
443
|
+
await loginRedirect(scopes);
|
|
539
444
|
onSuccess?.();
|
|
540
445
|
} catch (error) {
|
|
541
446
|
onError?.(error);
|
|
@@ -618,20 +523,15 @@ function SignOutButton({
|
|
|
618
523
|
text = "Sign out",
|
|
619
524
|
variant = "dark",
|
|
620
525
|
size = "medium",
|
|
621
|
-
useRedirect = false,
|
|
622
526
|
className = "",
|
|
623
527
|
style,
|
|
624
528
|
onSuccess,
|
|
625
529
|
onError
|
|
626
530
|
}) {
|
|
627
|
-
const {
|
|
531
|
+
const { logoutRedirect, inProgress } = useMsalAuth();
|
|
628
532
|
const handleClick = async () => {
|
|
629
533
|
try {
|
|
630
|
-
|
|
631
|
-
await logoutRedirect();
|
|
632
|
-
} else {
|
|
633
|
-
await logoutPopup();
|
|
634
|
-
}
|
|
534
|
+
await logoutRedirect();
|
|
635
535
|
onSuccess?.();
|
|
636
536
|
} catch (error) {
|
|
637
537
|
onError?.(error);
|
|
@@ -1116,28 +1016,23 @@ function AuthGuard({
|
|
|
1116
1016
|
children,
|
|
1117
1017
|
loadingComponent,
|
|
1118
1018
|
fallbackComponent,
|
|
1119
|
-
useRedirect = true,
|
|
1120
1019
|
scopes,
|
|
1121
1020
|
onAuthRequired
|
|
1122
1021
|
}) {
|
|
1123
|
-
const { isAuthenticated, inProgress, loginRedirect
|
|
1022
|
+
const { isAuthenticated, inProgress, loginRedirect } = useMsalAuth();
|
|
1124
1023
|
(0, import_react7.useEffect)(() => {
|
|
1125
1024
|
if (!isAuthenticated && !inProgress) {
|
|
1126
1025
|
onAuthRequired?.();
|
|
1127
1026
|
const login = async () => {
|
|
1128
1027
|
try {
|
|
1129
|
-
|
|
1130
|
-
await loginRedirect(scopes);
|
|
1131
|
-
} else {
|
|
1132
|
-
await loginPopup(scopes);
|
|
1133
|
-
}
|
|
1028
|
+
await loginRedirect(scopes);
|
|
1134
1029
|
} catch (error) {
|
|
1135
1030
|
console.error("[AuthGuard] Authentication failed:", error);
|
|
1136
1031
|
}
|
|
1137
1032
|
};
|
|
1138
1033
|
login();
|
|
1139
1034
|
}
|
|
1140
|
-
}, [isAuthenticated, inProgress,
|
|
1035
|
+
}, [isAuthenticated, inProgress, scopes, loginRedirect, onAuthRequired]);
|
|
1141
1036
|
if (inProgress) {
|
|
1142
1037
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: "Authenticating..." }) });
|
|
1143
1038
|
}
|
|
@@ -1707,7 +1602,6 @@ var import_msal_react3 = require("@azure/msal-react");
|
|
|
1707
1602
|
createScopedLogger,
|
|
1708
1603
|
getDebugLogger,
|
|
1709
1604
|
getMsalInstance,
|
|
1710
|
-
getPopupRedirectUri,
|
|
1711
1605
|
isValidAccountData,
|
|
1712
1606
|
isValidRedirectUri,
|
|
1713
1607
|
isValidScope,
|
package/dist/index.mjs
CHANGED
|
@@ -52,10 +52,6 @@ function validateScopes(scopes) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// src/utils/createMsalConfig.ts
|
|
55
|
-
var storedPopupRedirectUri;
|
|
56
|
-
function getPopupRedirectUri() {
|
|
57
|
-
return storedPopupRedirectUri;
|
|
58
|
-
}
|
|
59
55
|
function createMsalConfig(config) {
|
|
60
56
|
if (config.msalConfig) {
|
|
61
57
|
return config.msalConfig;
|
|
@@ -65,7 +61,6 @@ function createMsalConfig(config) {
|
|
|
65
61
|
tenantId,
|
|
66
62
|
authorityType = "common",
|
|
67
63
|
redirectUri,
|
|
68
|
-
popupRedirectUri,
|
|
69
64
|
postLogoutRedirectUri,
|
|
70
65
|
cacheLocation = "sessionStorage",
|
|
71
66
|
storeAuthStateInCookie = false,
|
|
@@ -88,8 +83,6 @@ function createMsalConfig(config) {
|
|
|
88
83
|
};
|
|
89
84
|
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
90
85
|
const finalRedirectUri = redirectUri || defaultRedirectUri;
|
|
91
|
-
const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
|
|
92
|
-
storedPopupRedirectUri = finalPopupRedirectUri;
|
|
93
86
|
if (allowedRedirectUris && allowedRedirectUris.length > 0) {
|
|
94
87
|
if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
|
|
95
88
|
throw new Error(
|
|
@@ -116,10 +109,6 @@ function createMsalConfig(config) {
|
|
|
116
109
|
storeAuthStateInCookie
|
|
117
110
|
},
|
|
118
111
|
system: {
|
|
119
|
-
windowHashTimeout: 6e4,
|
|
120
|
-
// Increase timeout for popup
|
|
121
|
-
iframeHashTimeout: 6e3,
|
|
122
|
-
loadFrameTimeout: 0,
|
|
123
112
|
loggerOptions: {
|
|
124
113
|
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
125
114
|
if (containsPii || !enableLogging) return;
|
|
@@ -154,17 +143,10 @@ function getMsalInstance() {
|
|
|
154
143
|
function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
|
|
155
144
|
const [msalInstance, setMsalInstance] = useState(null);
|
|
156
145
|
const instanceRef = useRef(null);
|
|
157
|
-
const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
|
|
158
146
|
useEffect(() => {
|
|
159
147
|
if (typeof window === "undefined") {
|
|
160
148
|
return;
|
|
161
149
|
}
|
|
162
|
-
if (isInPopup) {
|
|
163
|
-
if (config.enableLogging) {
|
|
164
|
-
console.log("[MSAL] Detected popup window - minimal initialization");
|
|
165
|
-
}
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
150
|
if (instanceRef.current) {
|
|
169
151
|
return;
|
|
170
152
|
}
|
|
@@ -173,17 +155,16 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
173
155
|
const msalConfig = createMsalConfig(config);
|
|
174
156
|
const instance = new PublicClientApplication(msalConfig);
|
|
175
157
|
await instance.initialize();
|
|
176
|
-
const isInPopup2 = window.opener && window.opener !== window;
|
|
177
158
|
try {
|
|
178
159
|
const response = await instance.handleRedirectPromise();
|
|
179
160
|
if (response) {
|
|
180
161
|
if (config.enableLogging) {
|
|
181
|
-
console.log("[MSAL] Redirect authentication successful"
|
|
162
|
+
console.log("[MSAL] Redirect authentication successful");
|
|
182
163
|
}
|
|
183
164
|
if (response.account) {
|
|
184
165
|
instance.setActiveAccount(response.account);
|
|
185
166
|
}
|
|
186
|
-
if (
|
|
167
|
+
if (window.location.hash) {
|
|
187
168
|
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
188
169
|
}
|
|
189
170
|
}
|
|
@@ -199,7 +180,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
199
180
|
} else {
|
|
200
181
|
console.error("[MSAL] Redirect handling error:", redirectError);
|
|
201
182
|
}
|
|
202
|
-
if (
|
|
183
|
+
if (window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
|
|
203
184
|
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
204
185
|
}
|
|
205
186
|
}
|
|
@@ -255,9 +236,6 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
255
236
|
if (typeof window === "undefined") {
|
|
256
237
|
return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
|
|
257
238
|
}
|
|
258
|
-
if (isInPopup) {
|
|
259
|
-
return /* @__PURE__ */ jsx("div", { style: { display: "none" }, children: "Completing authentication..." });
|
|
260
|
-
}
|
|
261
239
|
if (!msalInstance) {
|
|
262
240
|
return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
|
|
263
241
|
}
|
|
@@ -273,42 +251,12 @@ function MSALProvider({ children, ...props }) {
|
|
|
273
251
|
// src/hooks/useMsalAuth.ts
|
|
274
252
|
import { useMsal, useAccount } from "@azure/msal-react";
|
|
275
253
|
import { InteractionStatus } from "@azure/msal-browser";
|
|
276
|
-
import { useCallback, useMemo
|
|
254
|
+
import { useCallback, useMemo } from "react";
|
|
277
255
|
var pendingTokenRequests = /* @__PURE__ */ new Map();
|
|
278
256
|
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
279
257
|
const { instance, accounts, inProgress } = useMsal();
|
|
280
258
|
const account = useAccount(accounts[0] || null);
|
|
281
|
-
const popupInProgressRef = useRef2(false);
|
|
282
259
|
const isAuthenticated = useMemo(() => accounts.length > 0, [accounts]);
|
|
283
|
-
const loginPopup = useCallback(
|
|
284
|
-
async (scopes = defaultScopes) => {
|
|
285
|
-
if (inProgress !== InteractionStatus.None) {
|
|
286
|
-
console.warn("[MSAL] Interaction already in progress");
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
const popupRedirectUri = getPopupRedirectUri();
|
|
291
|
-
const request = {
|
|
292
|
-
scopes,
|
|
293
|
-
prompt: "select_account",
|
|
294
|
-
// Use popup-specific redirect URI (defaults to /blank.html)
|
|
295
|
-
redirectUri: popupRedirectUri
|
|
296
|
-
};
|
|
297
|
-
const response = await instance.loginPopup(request);
|
|
298
|
-
if (response?.account) {
|
|
299
|
-
instance.setActiveAccount(response.account);
|
|
300
|
-
}
|
|
301
|
-
} catch (error) {
|
|
302
|
-
if (error?.errorCode === "user_cancelled") {
|
|
303
|
-
console.log("[MSAL] User cancelled login");
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
console.error("[MSAL] Login popup failed:", error);
|
|
307
|
-
throw error;
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
[instance, defaultScopes, inProgress]
|
|
311
|
-
);
|
|
312
260
|
const loginRedirect = useCallback(
|
|
313
261
|
async (scopes = defaultScopes) => {
|
|
314
262
|
if (inProgress !== InteractionStatus.None) {
|
|
@@ -332,16 +280,6 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
332
280
|
},
|
|
333
281
|
[instance, defaultScopes, inProgress]
|
|
334
282
|
);
|
|
335
|
-
const logoutPopup = useCallback(async () => {
|
|
336
|
-
try {
|
|
337
|
-
await instance.logoutPopup({
|
|
338
|
-
account: account || void 0
|
|
339
|
-
});
|
|
340
|
-
} catch (error) {
|
|
341
|
-
console.error("[MSAL] Logout popup failed:", error);
|
|
342
|
-
throw error;
|
|
343
|
-
}
|
|
344
|
-
}, [instance, account]);
|
|
345
283
|
const logoutRedirect = useCallback(async () => {
|
|
346
284
|
try {
|
|
347
285
|
await instance.logoutRedirect({
|
|
@@ -372,31 +310,6 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
372
310
|
},
|
|
373
311
|
[instance, account, defaultScopes]
|
|
374
312
|
);
|
|
375
|
-
const acquireTokenPopup = useCallback(
|
|
376
|
-
async (scopes = defaultScopes) => {
|
|
377
|
-
if (!account) {
|
|
378
|
-
throw new Error("[MSAL] No active account. Please login first.");
|
|
379
|
-
}
|
|
380
|
-
if (popupInProgressRef.current) {
|
|
381
|
-
throw new Error("[MSAL] Popup already in progress. Please wait.");
|
|
382
|
-
}
|
|
383
|
-
try {
|
|
384
|
-
popupInProgressRef.current = true;
|
|
385
|
-
const request = {
|
|
386
|
-
scopes,
|
|
387
|
-
account
|
|
388
|
-
};
|
|
389
|
-
const response = await instance.acquireTokenPopup(request);
|
|
390
|
-
return response.accessToken;
|
|
391
|
-
} catch (error) {
|
|
392
|
-
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
393
|
-
throw error;
|
|
394
|
-
} finally {
|
|
395
|
-
popupInProgressRef.current = false;
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
[instance, account, defaultScopes]
|
|
399
|
-
);
|
|
400
313
|
const acquireTokenRedirect = useCallback(
|
|
401
314
|
async (scopes = defaultScopes) => {
|
|
402
315
|
if (!account) {
|
|
@@ -426,8 +339,9 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
426
339
|
try {
|
|
427
340
|
return await acquireTokenSilent(scopes);
|
|
428
341
|
} catch (error) {
|
|
429
|
-
console.warn("[MSAL] Silent token acquisition failed, falling back to
|
|
430
|
-
|
|
342
|
+
console.warn("[MSAL] Silent token acquisition failed, falling back to redirect");
|
|
343
|
+
await acquireTokenRedirect(scopes);
|
|
344
|
+
throw new Error("[MSAL] Redirecting for token acquisition");
|
|
431
345
|
} finally {
|
|
432
346
|
pendingTokenRequests.delete(requestKey);
|
|
433
347
|
}
|
|
@@ -435,7 +349,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
435
349
|
pendingTokenRequests.set(requestKey, tokenRequest);
|
|
436
350
|
return tokenRequest;
|
|
437
351
|
},
|
|
438
|
-
[acquireTokenSilent,
|
|
352
|
+
[acquireTokenSilent, acquireTokenRedirect, defaultScopes, account]
|
|
439
353
|
);
|
|
440
354
|
const clearSession = useCallback(async () => {
|
|
441
355
|
instance.setActiveAccount(null);
|
|
@@ -446,13 +360,10 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
446
360
|
accounts,
|
|
447
361
|
isAuthenticated,
|
|
448
362
|
inProgress: inProgress !== InteractionStatus.None,
|
|
449
|
-
loginPopup,
|
|
450
363
|
loginRedirect,
|
|
451
|
-
logoutPopup,
|
|
452
364
|
logoutRedirect,
|
|
453
365
|
acquireToken,
|
|
454
366
|
acquireTokenSilent,
|
|
455
|
-
acquireTokenPopup,
|
|
456
367
|
acquireTokenRedirect,
|
|
457
368
|
clearSession
|
|
458
369
|
};
|
|
@@ -465,23 +376,18 @@ function MicrosoftSignInButton({
|
|
|
465
376
|
text = "Sign in with Microsoft",
|
|
466
377
|
variant = "dark",
|
|
467
378
|
size = "medium",
|
|
468
|
-
useRedirect = true,
|
|
469
379
|
scopes,
|
|
470
380
|
className = "",
|
|
471
381
|
style,
|
|
472
382
|
onSuccess,
|
|
473
383
|
onError
|
|
474
384
|
}) {
|
|
475
|
-
const {
|
|
385
|
+
const { loginRedirect, inProgress } = useMsalAuth();
|
|
476
386
|
const [isLoading, setIsLoading] = useState2(false);
|
|
477
387
|
const handleClick = async () => {
|
|
478
388
|
setIsLoading(true);
|
|
479
389
|
try {
|
|
480
|
-
|
|
481
|
-
await loginRedirect(scopes);
|
|
482
|
-
} else {
|
|
483
|
-
await loginPopup(scopes);
|
|
484
|
-
}
|
|
390
|
+
await loginRedirect(scopes);
|
|
485
391
|
onSuccess?.();
|
|
486
392
|
} catch (error) {
|
|
487
393
|
onError?.(error);
|
|
@@ -564,20 +470,15 @@ function SignOutButton({
|
|
|
564
470
|
text = "Sign out",
|
|
565
471
|
variant = "dark",
|
|
566
472
|
size = "medium",
|
|
567
|
-
useRedirect = false,
|
|
568
473
|
className = "",
|
|
569
474
|
style,
|
|
570
475
|
onSuccess,
|
|
571
476
|
onError
|
|
572
477
|
}) {
|
|
573
|
-
const {
|
|
478
|
+
const { logoutRedirect, inProgress } = useMsalAuth();
|
|
574
479
|
const handleClick = async () => {
|
|
575
480
|
try {
|
|
576
|
-
|
|
577
|
-
await logoutRedirect();
|
|
578
|
-
} else {
|
|
579
|
-
await logoutPopup();
|
|
580
|
-
}
|
|
481
|
+
await logoutRedirect();
|
|
581
482
|
onSuccess?.();
|
|
582
483
|
} catch (error) {
|
|
583
484
|
onError?.(error);
|
|
@@ -1062,28 +963,23 @@ function AuthGuard({
|
|
|
1062
963
|
children,
|
|
1063
964
|
loadingComponent,
|
|
1064
965
|
fallbackComponent,
|
|
1065
|
-
useRedirect = true,
|
|
1066
966
|
scopes,
|
|
1067
967
|
onAuthRequired
|
|
1068
968
|
}) {
|
|
1069
|
-
const { isAuthenticated, inProgress, loginRedirect
|
|
969
|
+
const { isAuthenticated, inProgress, loginRedirect } = useMsalAuth();
|
|
1070
970
|
useEffect4(() => {
|
|
1071
971
|
if (!isAuthenticated && !inProgress) {
|
|
1072
972
|
onAuthRequired?.();
|
|
1073
973
|
const login = async () => {
|
|
1074
974
|
try {
|
|
1075
|
-
|
|
1076
|
-
await loginRedirect(scopes);
|
|
1077
|
-
} else {
|
|
1078
|
-
await loginPopup(scopes);
|
|
1079
|
-
}
|
|
975
|
+
await loginRedirect(scopes);
|
|
1080
976
|
} catch (error) {
|
|
1081
977
|
console.error("[AuthGuard] Authentication failed:", error);
|
|
1082
978
|
}
|
|
1083
979
|
};
|
|
1084
980
|
login();
|
|
1085
981
|
}
|
|
1086
|
-
}, [isAuthenticated, inProgress,
|
|
982
|
+
}, [isAuthenticated, inProgress, scopes, loginRedirect, onAuthRequired]);
|
|
1087
983
|
if (inProgress) {
|
|
1088
984
|
return /* @__PURE__ */ jsx7(Fragment3, { children: loadingComponent || /* @__PURE__ */ jsx7("div", { children: "Authenticating..." }) });
|
|
1089
985
|
}
|
|
@@ -1652,7 +1548,6 @@ export {
|
|
|
1652
1548
|
createScopedLogger,
|
|
1653
1549
|
getDebugLogger,
|
|
1654
1550
|
getMsalInstance,
|
|
1655
|
-
getPopupRedirectUri,
|
|
1656
1551
|
isValidAccountData,
|
|
1657
1552
|
isValidRedirectUri,
|
|
1658
1553
|
isValidScope,
|
package/package.json
CHANGED