@chemmangat/msal-next 3.1.5 → 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
@@ -4,29 +4,55 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [3.1.5] - 2026-03-05
6
6
 
7
- ### New Feature - Separate Popup Redirect URI
7
+ ### 🔄 Breaking Change - Redirect Flow by Default
8
8
 
9
- **Added `popupRedirectUri` prop** - Now you can have different redirect URIs for popup vs redirect flows!
9
+ **MicrosoftSignInButton now uses redirect flow by default** instead of popup flow.
10
10
 
11
- **Changes:**
12
- - Added `popupRedirectUri` prop to MSALProvider (defaults to `/blank.html`)
13
- - `redirectUri` prop is now used only for redirect flow
14
- - Popup authentication automatically uses `popupRedirectUri`
15
- - Exported `getPopupRedirectUri()` utility function
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:**
16
18
 
17
- **Usage:**
19
+ If you were using the default popup behavior:
18
20
  ```tsx
19
- <MSALProvider
20
- clientId="..."
21
- redirectUri="/auth/callback" // For redirect flow
22
- popupRedirectUri="/blank.html" // For popup flow (default)
23
- >
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>
24
49
  ```
25
50
 
26
51
  **Benefits:**
27
- - Use your custom redirect URI for redirect flow
28
- - Popup always uses blank.html (no full app loading in popup)
29
- - More flexible configuration
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}`)
30
56
 
31
57
  ## [3.1.4] - 2026-03-05
32
58
 
package/README.md CHANGED
@@ -91,23 +91,7 @@ npm install @chemmangat/msal-next@latest @azure/msal-browser@^4.0.0 @azure/msal-
91
91
 
92
92
  > **Important:** Use `MSALProvider` (not `MsalAuthProvider`) in your layout.tsx to avoid the "createContext only works in Client Components" error.
93
93
 
94
- ### 1. Create blank.html for popup authentication
95
-
96
- Create `public/blank.html`:
97
-
98
- ```html
99
- <!DOCTYPE html>
100
- <html>
101
- <head><title>Auth</title></head>
102
- <body></body>
103
- </html>
104
- ```
105
-
106
- Add to Azure AD redirect URIs:
107
- - `http://localhost:3000/blank.html`
108
- - `https://yourdomain.com/blank.html`
109
-
110
- ### 2. Wrap your app with MSALProvider
94
+ ### 1. Wrap your app with MSALProvider
111
95
 
112
96
  ```tsx
113
97
  // app/layout.tsx
@@ -120,9 +104,6 @@ export default function RootLayout({ children }) {
120
104
  <MSALProvider
121
105
  clientId={process.env.NEXT_PUBLIC_AZURE_AD_CLIENT_ID!}
122
106
  tenantId={process.env.NEXT_PUBLIC_AZURE_AD_TENANT_ID!}
123
- // Optional: specify custom redirect URIs
124
- // redirectUri="/auth/callback" // For redirect flow
125
- // popupRedirectUri="/blank.html" // For popup flow (default)
126
107
  >
127
108
  {children}
128
109
  </MSALProvider>
@@ -132,7 +113,7 @@ export default function RootLayout({ children }) {
132
113
  }
133
114
  ```
134
115
 
135
- ### 3. Add sign-in button
116
+ ### 2. Add sign-in button
136
117
 
137
118
  ```tsx
138
119
  // app/page.tsx
@@ -153,6 +134,60 @@ export default function Home() {
153
134
 
154
135
  That's it! 🎉
155
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
+
156
191
  ## Components
157
192
 
158
193
  ### MSALProvider (Recommended for App Router)
@@ -205,15 +240,18 @@ export function MyProviders({ children }) {
205
240
 
206
241
  ### MicrosoftSignInButton
207
242
 
208
- Pre-styled sign-in button with Microsoft branding.
243
+ Pre-styled sign-in button with Microsoft branding. Uses redirect flow by default (no popups).
209
244
 
210
245
  ```tsx
211
246
  <MicrosoftSignInButton
212
247
  variant="dark" // or "light"
213
248
  size="medium" // "small", "medium", "large"
214
- useRedirect={false} // Use popup by default
249
+ useRedirect={true} // Default: true (full page redirect)
215
250
  onSuccess={() => console.log('Signed in!')}
216
251
  />
252
+
253
+ // If you prefer popup (requires blank.html setup):
254
+ <MicrosoftSignInButton useRedirect={false} />
217
255
  ```
218
256
 
219
257
  ### SignOutButton
package/dist/index.d.mts CHANGED
@@ -42,8 +42,9 @@ interface MsalAuthConfig {
42
42
  */
43
43
  redirectUri?: string;
44
44
  /**
45
- * Redirect URI for popup authentication (recommended: /blank.html)
46
- * If not specified, uses redirectUri
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)
47
48
  * @default redirectUri
48
49
  */
49
50
  popupRedirectUri?: string;
@@ -109,7 +110,7 @@ interface MsalAuthProviderProps extends MsalAuthConfig {
109
110
  * @returns The MSAL instance or null if not initialized
110
111
  */
111
112
  declare function getMsalInstance(): PublicClientApplication | null;
112
- declare function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element | null;
113
+ declare function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element;
113
114
 
114
115
  /**
115
116
  * Pre-configured MSALProvider component for Next.js App Router layouts.
@@ -157,7 +158,7 @@ interface MicrosoftSignInButtonProps {
157
158
  size?: 'small' | 'medium' | 'large';
158
159
  /**
159
160
  * Use redirect flow instead of popup
160
- * @default false
161
+ * @default true
161
162
  */
162
163
  useRedirect?: boolean;
163
164
  /**
package/dist/index.d.ts CHANGED
@@ -42,8 +42,9 @@ interface MsalAuthConfig {
42
42
  */
43
43
  redirectUri?: string;
44
44
  /**
45
- * Redirect URI for popup authentication (recommended: /blank.html)
46
- * If not specified, uses redirectUri
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)
47
48
  * @default redirectUri
48
49
  */
49
50
  popupRedirectUri?: string;
@@ -109,7 +110,7 @@ interface MsalAuthProviderProps extends MsalAuthConfig {
109
110
  * @returns The MSAL instance or null if not initialized
110
111
  */
111
112
  declare function getMsalInstance(): PublicClientApplication | null;
112
- declare function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element | null;
113
+ declare function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element;
113
114
 
114
115
  /**
115
116
  * Pre-configured MSALProvider component for Next.js App Router layouts.
@@ -157,7 +158,7 @@ interface MicrosoftSignInButtonProps {
157
158
  size?: 'small' | 'medium' | 'large';
158
159
  /**
159
160
  * Use redirect flow instead of popup
160
- * @default false
161
+ * @default true
161
162
  */
162
163
  useRedirect?: boolean;
163
164
  /**
package/dist/index.js CHANGED
@@ -142,8 +142,7 @@ function createMsalConfig(config) {
142
142
  };
143
143
  const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
144
144
  const finalRedirectUri = redirectUri || defaultRedirectUri;
145
- const defaultPopupRedirectUri = typeof window !== "undefined" ? `${window.location.origin}/blank.html` : "http://localhost:3000/blank.html";
146
- const finalPopupRedirectUri = popupRedirectUri || defaultPopupRedirectUri;
145
+ const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
147
146
  storedPopupRedirectUri = finalPopupRedirectUri;
148
147
  if (allowedRedirectUris && allowedRedirectUris.length > 0) {
149
148
  if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
@@ -209,12 +208,12 @@ function getMsalInstance() {
209
208
  function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
210
209
  const [msalInstance, setMsalInstance] = (0, import_react.useState)(null);
211
210
  const instanceRef = (0, import_react.useRef)(null);
211
+ const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
212
212
  (0, import_react.useEffect)(() => {
213
213
  if (typeof window === "undefined") {
214
214
  return;
215
215
  }
216
- const isInPopup2 = window.opener && window.opener !== window;
217
- if (isInPopup2) {
216
+ if (isInPopup) {
218
217
  if (config.enableLogging) {
219
218
  console.log("[MSAL] Detected popup window - minimal initialization");
220
219
  }
@@ -228,17 +227,17 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
228
227
  const msalConfig = createMsalConfig(config);
229
228
  const instance = new import_msal_browser2.PublicClientApplication(msalConfig);
230
229
  await instance.initialize();
231
- const isInPopup3 = window.opener && window.opener !== window;
230
+ const isInPopup2 = window.opener && window.opener !== window;
232
231
  try {
233
232
  const response = await instance.handleRedirectPromise();
234
233
  if (response) {
235
234
  if (config.enableLogging) {
236
- console.log("[MSAL] Redirect authentication successful", isInPopup3 ? "(popup)" : "(main)");
235
+ console.log("[MSAL] Redirect authentication successful", isInPopup2 ? "(popup)" : "(main)");
237
236
  }
238
237
  if (response.account) {
239
238
  instance.setActiveAccount(response.account);
240
239
  }
241
- if (!isInPopup3 && window.location.hash) {
240
+ if (!isInPopup2 && window.location.hash) {
242
241
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
243
242
  }
244
243
  }
@@ -254,7 +253,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
254
253
  } else {
255
254
  console.error("[MSAL] Redirect handling error:", redirectError);
256
255
  }
257
- if (!isInPopup3 && 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="))) {
258
257
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
259
258
  }
260
259
  }
@@ -310,9 +309,8 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
310
309
  if (typeof window === "undefined") {
311
310
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
312
311
  }
313
- const isInPopup = window.opener && window.opener !== window;
314
312
  if (isInPopup) {
315
- return null;
313
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "none" }, children: "Completing authentication..." });
316
314
  }
317
315
  if (!msalInstance) {
318
316
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
@@ -521,7 +519,7 @@ function MicrosoftSignInButton({
521
519
  text = "Sign in with Microsoft",
522
520
  variant = "dark",
523
521
  size = "medium",
524
- useRedirect = false,
522
+ useRedirect = true,
525
523
  scopes,
526
524
  className = "",
527
525
  style,
package/dist/index.mjs CHANGED
@@ -88,8 +88,7 @@ function createMsalConfig(config) {
88
88
  };
89
89
  const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
90
90
  const finalRedirectUri = redirectUri || defaultRedirectUri;
91
- const defaultPopupRedirectUri = typeof window !== "undefined" ? `${window.location.origin}/blank.html` : "http://localhost:3000/blank.html";
92
- const finalPopupRedirectUri = popupRedirectUri || defaultPopupRedirectUri;
91
+ const finalPopupRedirectUri = popupRedirectUri || finalRedirectUri;
93
92
  storedPopupRedirectUri = finalPopupRedirectUri;
94
93
  if (allowedRedirectUris && allowedRedirectUris.length > 0) {
95
94
  if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
@@ -155,12 +154,12 @@ function getMsalInstance() {
155
154
  function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
156
155
  const [msalInstance, setMsalInstance] = useState(null);
157
156
  const instanceRef = useRef(null);
157
+ const isInPopup = typeof window !== "undefined" && window.opener && window.opener !== window;
158
158
  useEffect(() => {
159
159
  if (typeof window === "undefined") {
160
160
  return;
161
161
  }
162
- const isInPopup2 = window.opener && window.opener !== window;
163
- if (isInPopup2) {
162
+ if (isInPopup) {
164
163
  if (config.enableLogging) {
165
164
  console.log("[MSAL] Detected popup window - minimal initialization");
166
165
  }
@@ -174,17 +173,17 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
174
173
  const msalConfig = createMsalConfig(config);
175
174
  const instance = new PublicClientApplication(msalConfig);
176
175
  await instance.initialize();
177
- const isInPopup3 = window.opener && window.opener !== window;
176
+ const isInPopup2 = window.opener && window.opener !== window;
178
177
  try {
179
178
  const response = await instance.handleRedirectPromise();
180
179
  if (response) {
181
180
  if (config.enableLogging) {
182
- console.log("[MSAL] Redirect authentication successful", isInPopup3 ? "(popup)" : "(main)");
181
+ console.log("[MSAL] Redirect authentication successful", isInPopup2 ? "(popup)" : "(main)");
183
182
  }
184
183
  if (response.account) {
185
184
  instance.setActiveAccount(response.account);
186
185
  }
187
- if (!isInPopup3 && window.location.hash) {
186
+ if (!isInPopup2 && window.location.hash) {
188
187
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
189
188
  }
190
189
  }
@@ -200,7 +199,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
200
199
  } else {
201
200
  console.error("[MSAL] Redirect handling error:", redirectError);
202
201
  }
203
- if (!isInPopup3 && 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="))) {
204
203
  window.history.replaceState(null, "", window.location.pathname + window.location.search);
205
204
  }
206
205
  }
@@ -256,9 +255,8 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
256
255
  if (typeof window === "undefined") {
257
256
  return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
258
257
  }
259
- const isInPopup = window.opener && window.opener !== window;
260
258
  if (isInPopup) {
261
- return null;
259
+ return /* @__PURE__ */ jsx("div", { style: { display: "none" }, children: "Completing authentication..." });
262
260
  }
263
261
  if (!msalInstance) {
264
262
  return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
@@ -467,7 +465,7 @@ function MicrosoftSignInButton({
467
465
  text = "Sign in with Microsoft",
468
466
  variant = "dark",
469
467
  size = "medium",
470
- useRedirect = false,
468
+ useRedirect = true,
471
469
  scopes,
472
470
  className = "",
473
471
  style,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chemmangat/msal-next",
3
- "version": "3.1.5",
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",