@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 +42 -16
- package/README.md +61 -23
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +9 -11
- package/dist/index.mjs +9 -11
- package/package.json +1 -1
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
|
-
###
|
|
7
|
+
### 🔄 Breaking Change - Redirect Flow by Default
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**MicrosoftSignInButton now uses redirect flow by default** instead of popup flow.
|
|
10
10
|
|
|
11
|
-
**
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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
|
-
|
|
19
|
+
If you were using the default popup behavior:
|
|
18
20
|
```tsx
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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.
|
|
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
|
-
###
|
|
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={
|
|
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 (
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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",
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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",
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 =
|
|
468
|
+
useRedirect = true,
|
|
471
469
|
scopes,
|
|
472
470
|
className = "",
|
|
473
471
|
style,
|
package/package.json
CHANGED