@chemmangat/msal-next 3.1.2 → 3.1.6

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 CHANGED
@@ -2,6 +2,123 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [3.1.5] - 2026-03-05
6
+
7
+ ### 🔄 Breaking Change - Redirect Flow by Default
8
+
9
+ **MicrosoftSignInButton now uses redirect flow by default** instead of popup flow.
10
+
11
+ **Why this change?**
12
+ - Popup flow requires additional setup (blank.html page)
13
+ - Popup flow causes the full app to load in popup window
14
+ - Redirect flow is simpler and works out of the box
15
+ - Most users prefer redirect flow for better UX
16
+
17
+ **Migration:**
18
+
19
+ If you were using the default popup behavior:
20
+ ```tsx
21
+ // Before (v3.1.4 and earlier) - used popup by default
22
+ <MicrosoftSignInButton />
23
+
24
+ // After (v3.1.5) - uses redirect by default
25
+ <MicrosoftSignInButton /> // Now redirects full page
26
+
27
+ // To keep popup behavior:
28
+ <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
+
37
+ ### ✨ Optional Popup Support
38
+
39
+ **Added optional `popupRedirectUri` prop** - Only use if you prefer popup flow.
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>
49
+ ```
50
+
51
+ **Benefits:**
52
+ - Simpler default experience (no setup required)
53
+ - Redirect flow works out of the box
54
+ - Popup flow still available for those who need it
55
+ - Backward compatible (just set `useRedirect={false}`)
56
+
57
+ ## [3.1.4] - 2026-03-05
58
+
59
+ ### 🔧 Configuration Fix - Popup Redirect
60
+
61
+ **BREAKING CHANGE:** You must now create a `public/blank.html` file and configure it as a redirect URI in Azure AD.
62
+
63
+ **Why:** This prevents your full app from loading in the popup window after authentication.
64
+
65
+ **Setup Required:**
66
+
67
+ 1. Create `public/blank.html`:
68
+ ```html
69
+ <!DOCTYPE html>
70
+ <html>
71
+ <head><title>Auth</title></head>
72
+ <body></body>
73
+ </html>
74
+ ```
75
+
76
+ 2. Add to Azure AD redirect URIs:
77
+ - `http://localhost:3000/blank.html`
78
+ - `https://yourdomain.com/blank.html`
79
+
80
+ 3. Update your MSALProvider:
81
+ ```tsx
82
+ <MSALProvider
83
+ clientId="..."
84
+ redirectUri={typeof window !== 'undefined' ? `${window.location.origin}/blank.html` : undefined}
85
+ >
86
+ ```
87
+
88
+ **Changes:**
89
+ - Set `navigateToLoginRequestUrl` default to `false`
90
+ - Updated README with blank.html setup instructions
91
+ - Added PUBLIC_BLANK_HTML.md guide
92
+
93
+ ## [3.1.3] - 2026-03-05
94
+
95
+ ### 🐛 Critical Fix - Popup Redirect Issue
96
+
97
+ **Fixed:** Popup window now stays as popup and doesn't navigate to redirect URI.
98
+
99
+ **Changes:**
100
+ - Added `windowHashTimeout`, `iframeHashTimeout`, and `loadFrameTimeout` to MSAL config
101
+ - Set `redirectUri: undefined` in popup requests to prevent navigation
102
+ - Popup now properly closes after authentication without showing redirect page
103
+
104
+ **This fixes the issue where the redirect URI was opening inside the popup window instead of the popup closing.**
105
+
106
+ ## [3.1.2] - 2026-03-05
107
+
108
+ ### 📝 Documentation Update
109
+
110
+ - Updated README with current version number
111
+ - No code changes from 3.1.1
112
+
113
+ ## [3.1.1] - 2026-03-05
114
+
115
+ ### 📝 Documentation & Build
116
+
117
+ - Fixed landing page build issues
118
+ - Cleaned up redundant documentation files
119
+ - Updated package files list
120
+ - No code changes from 3.0.8
121
+
5
122
  ## [3.0.8] - 2026-03-05
6
123
 
7
124
  ### 🚨 CRITICAL BUG FIX
package/README.md CHANGED
@@ -5,7 +5,7 @@ Production-grade MSAL authentication library for Next.js App Router with minimal
5
5
  [![npm version](https://badge.fury.io/js/@chemmangat%2Fmsal-next.svg)](https://www.npmjs.com/package/@chemmangat/msal-next)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- > **📦 Current Version: 3.1.2** - Critical bug fixes for popup authentication. [See changelog](./CHANGELOG.md)
8
+ > **📦 Current Version: 3.1.5** - Separate popup redirect URI support. [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,6 +134,60 @@ export default function Home() {
134
134
 
135
135
  That's it! 🎉
136
136
 
137
+ The button uses redirect flow by default (full page redirect to Microsoft login, then back to your app). No popups, no blank.html needed.
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.
190
+
137
191
  ## Components
138
192
 
139
193
  ### MSALProvider (Recommended for App Router)
@@ -186,15 +240,18 @@ export function MyProviders({ children }) {
186
240
 
187
241
  ### MicrosoftSignInButton
188
242
 
189
- Pre-styled sign-in button with Microsoft branding.
243
+ Pre-styled sign-in button with Microsoft branding. Uses redirect flow by default (no popups).
190
244
 
191
245
  ```tsx
192
246
  <MicrosoftSignInButton
193
247
  variant="dark" // or "light"
194
248
  size="medium" // "small", "medium", "large"
195
- useRedirect={false} // Use popup by default
249
+ useRedirect={true} // Default: true (full page redirect)
196
250
  onSuccess={() => console.log('Signed in!')}
197
251
  />
252
+
253
+ // If you prefer popup (requires blank.html setup):
254
+ <MicrosoftSignInButton useRedirect={false} />
198
255
  ```
199
256
 
200
257
  ### SignOutButton
package/dist/index.d.mts CHANGED
@@ -41,6 +41,13 @@ 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;
44
51
  /**
45
52
  * Post logout redirect URI
46
53
  * @default redirectUri
@@ -151,7 +158,7 @@ interface MicrosoftSignInButtonProps {
151
158
  size?: 'small' | 'medium' | 'large';
152
159
  /**
153
160
  * Use redirect flow instead of popup
154
- * @default false
161
+ * @default true
155
162
  */
156
163
  useRedirect?: boolean;
157
164
  /**
@@ -584,6 +591,7 @@ interface UseRolesReturn {
584
591
  */
585
592
  declare function useRoles(): UseRolesReturn;
586
593
 
594
+ declare function getPopupRedirectUri(): string | undefined;
587
595
  declare function createMsalConfig(config: MsalAuthConfig): Configuration;
588
596
 
589
597
  interface WithAuthOptions extends Omit<AuthGuardProps, 'children'> {
@@ -931,4 +939,4 @@ interface ServerSession {
931
939
  accessToken?: string;
932
940
  }
933
941
 
934
- 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 };
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, getPopupRedirectUri, isValidAccountData, isValidRedirectUri, isValidScope, retryWithBackoff, safeJsonParse, sanitizeError, useGraphApi, useMsalAuth, useRoles, useUserProfile, validateScopes, withAuth };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,13 @@ 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;
44
51
  /**
45
52
  * Post logout redirect URI
46
53
  * @default redirectUri
@@ -151,7 +158,7 @@ interface MicrosoftSignInButtonProps {
151
158
  size?: 'small' | 'medium' | 'large';
152
159
  /**
153
160
  * Use redirect flow instead of popup
154
- * @default false
161
+ * @default true
155
162
  */
156
163
  useRedirect?: boolean;
157
164
  /**
@@ -584,6 +591,7 @@ interface UseRolesReturn {
584
591
  */
585
592
  declare function useRoles(): UseRolesReturn;
586
593
 
594
+ declare function getPopupRedirectUri(): string | undefined;
587
595
  declare function createMsalConfig(config: MsalAuthConfig): Configuration;
588
596
 
589
597
  interface WithAuthOptions extends Omit<AuthGuardProps, 'children'> {
@@ -931,4 +939,4 @@ interface ServerSession {
931
939
  accessToken?: string;
932
940
  }
933
941
 
934
- 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 };
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, getPopupRedirectUri, isValidAccountData, isValidRedirectUri, isValidScope, retryWithBackoff, safeJsonParse, sanitizeError, useGraphApi, useMsalAuth, useRoles, useUserProfile, validateScopes, withAuth };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(client_exports, {
35
35
  createScopedLogger: () => createScopedLogger,
36
36
  getDebugLogger: () => getDebugLogger,
37
37
  getMsalInstance: () => getMsalInstance,
38
+ getPopupRedirectUri: () => getPopupRedirectUri,
38
39
  isValidAccountData: () => isValidAccountData,
39
40
  isValidRedirectUri: () => isValidRedirectUri,
40
41
  isValidScope: () => isValidScope,
@@ -105,6 +106,10 @@ function validateScopes(scopes) {
105
106
  }
106
107
 
107
108
  // src/utils/createMsalConfig.ts
109
+ var storedPopupRedirectUri;
110
+ function getPopupRedirectUri() {
111
+ return storedPopupRedirectUri;
112
+ }
108
113
  function createMsalConfig(config) {
109
114
  if (config.msalConfig) {
110
115
  return config.msalConfig;
@@ -114,10 +119,11 @@ function createMsalConfig(config) {
114
119
  tenantId,
115
120
  authorityType = "common",
116
121
  redirectUri,
122
+ popupRedirectUri,
117
123
  postLogoutRedirectUri,
118
124
  cacheLocation = "sessionStorage",
119
125
  storeAuthStateInCookie = false,
120
- navigateToLoginRequestUrl = true,
126
+ navigateToLoginRequestUrl = false,
121
127
  enableLogging = false,
122
128
  loggerCallback,
123
129
  allowedRedirectUris
@@ -136,6 +142,8 @@ function createMsalConfig(config) {
136
142
  };
137
143
  const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
138
144
  const finalRedirectUri = redirectUri || defaultRedirectUri;
145
+ const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
146
+ storedPopupRedirectUri = finalPopupRedirectUri;
139
147
  if (allowedRedirectUris && allowedRedirectUris.length > 0) {
140
148
  if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
141
149
  throw new Error(
@@ -162,6 +170,10 @@ function createMsalConfig(config) {
162
170
  storeAuthStateInCookie
163
171
  },
164
172
  system: {
173
+ windowHashTimeout: 6e4,
174
+ // Increase timeout for popup
175
+ iframeHashTimeout: 6e3,
176
+ loadFrameTimeout: 0,
165
177
  loggerOptions: {
166
178
  loggerCallback: loggerCallback || ((level, message, containsPii) => {
167
179
  if (containsPii || !enableLogging) return;
@@ -196,10 +208,17 @@ function getMsalInstance() {
196
208
  function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
197
209
  const [msalInstance, setMsalInstance] = (0, import_react.useState)(null);
198
210
  const instanceRef = (0, import_react.useRef)(null);
211
+ const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
199
212
  (0, import_react.useEffect)(() => {
200
213
  if (typeof window === "undefined") {
201
214
  return;
202
215
  }
216
+ if (isInPopup) {
217
+ if (config.enableLogging) {
218
+ console.log("[MSAL] Detected popup window - minimal initialization");
219
+ }
220
+ return;
221
+ }
203
222
  if (instanceRef.current) {
204
223
  return;
205
224
  }
@@ -208,17 +227,17 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
208
227
  const msalConfig = createMsalConfig(config);
209
228
  const instance = new import_msal_browser2.PublicClientApplication(msalConfig);
210
229
  await instance.initialize();
211
- const isInPopup = window.opener && window.opener !== window;
230
+ const isInPopup2 = window.opener && window.opener !== window;
212
231
  try {
213
232
  const response = await instance.handleRedirectPromise();
214
233
  if (response) {
215
234
  if (config.enableLogging) {
216
- console.log("[MSAL] Redirect authentication successful", isInPopup ? "(popup)" : "(main)");
235
+ console.log("[MSAL] Redirect authentication successful", isInPopup2 ? "(popup)" : "(main)");
217
236
  }
218
237
  if (response.account) {
219
238
  instance.setActiveAccount(response.account);
220
239
  }
221
- if (!isInPopup && window.location.hash) {
240
+ if (!isInPopup2 && window.location.hash) {
222
241
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
223
242
  }
224
243
  }
@@ -234,7 +253,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
234
253
  } else {
235
254
  console.error("[MSAL] Redirect handling error:", redirectError);
236
255
  }
237
- if (!isInPopup && window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
256
+ if (!isInPopup2 && window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
238
257
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
239
258
  }
240
259
  }
@@ -290,6 +309,9 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
290
309
  if (typeof window === "undefined") {
291
310
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
292
311
  }
312
+ if (isInPopup) {
313
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "none" }, children: "Completing authentication..." });
314
+ }
293
315
  if (!msalInstance) {
294
316
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
295
317
  }
@@ -319,9 +341,12 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
319
341
  return;
320
342
  }
321
343
  try {
344
+ const popupRedirectUri = getPopupRedirectUri();
322
345
  const request = {
323
346
  scopes,
324
- prompt: "select_account"
347
+ prompt: "select_account",
348
+ // Use popup-specific redirect URI (defaults to /blank.html)
349
+ redirectUri: popupRedirectUri
325
350
  };
326
351
  const response = await instance.loginPopup(request);
327
352
  if (response?.account) {
@@ -494,7 +519,7 @@ function MicrosoftSignInButton({
494
519
  text = "Sign in with Microsoft",
495
520
  variant = "dark",
496
521
  size = "medium",
497
- useRedirect = false,
522
+ useRedirect = true,
498
523
  scopes,
499
524
  className = "",
500
525
  style,
@@ -1682,6 +1707,7 @@ var import_msal_react3 = require("@azure/msal-react");
1682
1707
  createScopedLogger,
1683
1708
  getDebugLogger,
1684
1709
  getMsalInstance,
1710
+ getPopupRedirectUri,
1685
1711
  isValidAccountData,
1686
1712
  isValidRedirectUri,
1687
1713
  isValidScope,
package/dist/index.mjs CHANGED
@@ -52,6 +52,10 @@ function validateScopes(scopes) {
52
52
  }
53
53
 
54
54
  // src/utils/createMsalConfig.ts
55
+ var storedPopupRedirectUri;
56
+ function getPopupRedirectUri() {
57
+ return storedPopupRedirectUri;
58
+ }
55
59
  function createMsalConfig(config) {
56
60
  if (config.msalConfig) {
57
61
  return config.msalConfig;
@@ -61,10 +65,11 @@ function createMsalConfig(config) {
61
65
  tenantId,
62
66
  authorityType = "common",
63
67
  redirectUri,
68
+ popupRedirectUri,
64
69
  postLogoutRedirectUri,
65
70
  cacheLocation = "sessionStorage",
66
71
  storeAuthStateInCookie = false,
67
- navigateToLoginRequestUrl = true,
72
+ navigateToLoginRequestUrl = false,
68
73
  enableLogging = false,
69
74
  loggerCallback,
70
75
  allowedRedirectUris
@@ -83,6 +88,8 @@ function createMsalConfig(config) {
83
88
  };
84
89
  const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
85
90
  const finalRedirectUri = redirectUri || defaultRedirectUri;
91
+ const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
92
+ storedPopupRedirectUri = finalPopupRedirectUri;
86
93
  if (allowedRedirectUris && allowedRedirectUris.length > 0) {
87
94
  if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
88
95
  throw new Error(
@@ -109,6 +116,10 @@ function createMsalConfig(config) {
109
116
  storeAuthStateInCookie
110
117
  },
111
118
  system: {
119
+ windowHashTimeout: 6e4,
120
+ // Increase timeout for popup
121
+ iframeHashTimeout: 6e3,
122
+ loadFrameTimeout: 0,
112
123
  loggerOptions: {
113
124
  loggerCallback: loggerCallback || ((level, message, containsPii) => {
114
125
  if (containsPii || !enableLogging) return;
@@ -143,10 +154,17 @@ function getMsalInstance() {
143
154
  function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
144
155
  const [msalInstance, setMsalInstance] = useState(null);
145
156
  const instanceRef = useRef(null);
157
+ const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
146
158
  useEffect(() => {
147
159
  if (typeof window === "undefined") {
148
160
  return;
149
161
  }
162
+ if (isInPopup) {
163
+ if (config.enableLogging) {
164
+ console.log("[MSAL] Detected popup window - minimal initialization");
165
+ }
166
+ return;
167
+ }
150
168
  if (instanceRef.current) {
151
169
  return;
152
170
  }
@@ -155,17 +173,17 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
155
173
  const msalConfig = createMsalConfig(config);
156
174
  const instance = new PublicClientApplication(msalConfig);
157
175
  await instance.initialize();
158
- const isInPopup = window.opener && window.opener !== window;
176
+ const isInPopup2 = window.opener && window.opener !== window;
159
177
  try {
160
178
  const response = await instance.handleRedirectPromise();
161
179
  if (response) {
162
180
  if (config.enableLogging) {
163
- console.log("[MSAL] Redirect authentication successful", isInPopup ? "(popup)" : "(main)");
181
+ console.log("[MSAL] Redirect authentication successful", isInPopup2 ? "(popup)" : "(main)");
164
182
  }
165
183
  if (response.account) {
166
184
  instance.setActiveAccount(response.account);
167
185
  }
168
- if (!isInPopup && window.location.hash) {
186
+ if (!isInPopup2 && window.location.hash) {
169
187
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
170
188
  }
171
189
  }
@@ -181,7 +199,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
181
199
  } else {
182
200
  console.error("[MSAL] Redirect handling error:", redirectError);
183
201
  }
184
- if (!isInPopup && window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
202
+ if (!isInPopup2 && window.location.hash && (window.location.hash.includes("code=") || window.location.hash.includes("error="))) {
185
203
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
186
204
  }
187
205
  }
@@ -237,6 +255,9 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
237
255
  if (typeof window === "undefined") {
238
256
  return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
239
257
  }
258
+ if (isInPopup) {
259
+ return /* @__PURE__ */ jsx("div", { style: { display: "none" }, children: "Completing authentication..." });
260
+ }
240
261
  if (!msalInstance) {
241
262
  return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
242
263
  }
@@ -266,9 +287,12 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
266
287
  return;
267
288
  }
268
289
  try {
290
+ const popupRedirectUri = getPopupRedirectUri();
269
291
  const request = {
270
292
  scopes,
271
- prompt: "select_account"
293
+ prompt: "select_account",
294
+ // Use popup-specific redirect URI (defaults to /blank.html)
295
+ redirectUri: popupRedirectUri
272
296
  };
273
297
  const response = await instance.loginPopup(request);
274
298
  if (response?.account) {
@@ -441,7 +465,7 @@ function MicrosoftSignInButton({
441
465
  text = "Sign in with Microsoft",
442
466
  variant = "dark",
443
467
  size = "medium",
444
- useRedirect = false,
468
+ useRedirect = true,
445
469
  scopes,
446
470
  className = "",
447
471
  style,
@@ -1628,6 +1652,7 @@ export {
1628
1652
  createScopedLogger,
1629
1653
  getDebugLogger,
1630
1654
  getMsalInstance,
1655
+ getPopupRedirectUri,
1631
1656
  isValidAccountData,
1632
1657
  isValidRedirectUri,
1633
1658
  isValidScope,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chemmangat/msal-next",
3
- "version": "3.1.2",
3
+ "version": "3.1.6",
4
4
  "description": "Production-grade MSAL authentication package for Next.js App Router with minimal boilerplate",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",