@chemmangat/msal-next 2.1.2 → 2.3.0
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/dist/index.js +2 -1415
- package/dist/index.mjs +2 -1378
- package/dist/server.js +1 -91
- package/dist/server.mjs +1 -88
- package/package.json +1 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/server.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1415 +1,2 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var msalReact = require('@azure/msal-react');
|
|
4
|
-
var msalBrowser = require('@azure/msal-browser');
|
|
5
|
-
var react = require('react');
|
|
6
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
-
var server = require('next/server');
|
|
8
|
-
|
|
9
|
-
// src/utils/validation.ts
|
|
10
|
-
function safeJsonParse(jsonString, validator) {
|
|
11
|
-
try {
|
|
12
|
-
const parsed = JSON.parse(jsonString);
|
|
13
|
-
if (validator(parsed)) {
|
|
14
|
-
return parsed;
|
|
15
|
-
}
|
|
16
|
-
console.warn("[Validation] JSON validation failed");
|
|
17
|
-
return null;
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error("[Validation] JSON parse error:", error);
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function isValidAccountData(data) {
|
|
24
|
-
return typeof data === "object" && data !== null && typeof data.homeAccountId === "string" && data.homeAccountId.length > 0 && typeof data.username === "string" && data.username.length > 0 && (data.name === void 0 || typeof data.name === "string");
|
|
25
|
-
}
|
|
26
|
-
function sanitizeError(error) {
|
|
27
|
-
if (error instanceof Error) {
|
|
28
|
-
const message = error.message;
|
|
29
|
-
const sanitized = message.replace(/[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/g, "[TOKEN_REDACTED]").replace(/[a-f0-9]{32,}/gi, "[SECRET_REDACTED]").replace(/Bearer\s+[^\s]+/gi, "Bearer [REDACTED]");
|
|
30
|
-
return sanitized;
|
|
31
|
-
}
|
|
32
|
-
return "An unexpected error occurred";
|
|
33
|
-
}
|
|
34
|
-
function isValidRedirectUri(uri, allowedOrigins) {
|
|
35
|
-
try {
|
|
36
|
-
const url = new URL(uri);
|
|
37
|
-
return allowedOrigins.some((allowed) => {
|
|
38
|
-
const allowedUrl = new URL(allowed);
|
|
39
|
-
return url.origin === allowedUrl.origin;
|
|
40
|
-
});
|
|
41
|
-
} catch {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function isValidScope(scope) {
|
|
46
|
-
return /^[a-zA-Z0-9._-]+$/.test(scope);
|
|
47
|
-
}
|
|
48
|
-
function validateScopes(scopes) {
|
|
49
|
-
return Array.isArray(scopes) && scopes.every(isValidScope);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/utils/createMsalConfig.ts
|
|
53
|
-
function createMsalConfig(config) {
|
|
54
|
-
if (config.msalConfig) {
|
|
55
|
-
return config.msalConfig;
|
|
56
|
-
}
|
|
57
|
-
const {
|
|
58
|
-
clientId,
|
|
59
|
-
tenantId,
|
|
60
|
-
authorityType = "common",
|
|
61
|
-
redirectUri,
|
|
62
|
-
postLogoutRedirectUri,
|
|
63
|
-
cacheLocation = "sessionStorage",
|
|
64
|
-
storeAuthStateInCookie = false,
|
|
65
|
-
navigateToLoginRequestUrl = true,
|
|
66
|
-
enableLogging = false,
|
|
67
|
-
loggerCallback,
|
|
68
|
-
allowedRedirectUris
|
|
69
|
-
} = config;
|
|
70
|
-
if (!clientId) {
|
|
71
|
-
throw new Error("@chemmangat/msal-next: clientId is required");
|
|
72
|
-
}
|
|
73
|
-
const getAuthority = () => {
|
|
74
|
-
if (authorityType === "tenant") {
|
|
75
|
-
if (!tenantId) {
|
|
76
|
-
throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is "tenant"');
|
|
77
|
-
}
|
|
78
|
-
return `https://login.microsoftonline.com/${tenantId}`;
|
|
79
|
-
}
|
|
80
|
-
return `https://login.microsoftonline.com/${authorityType}`;
|
|
81
|
-
};
|
|
82
|
-
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
83
|
-
const finalRedirectUri = redirectUri || defaultRedirectUri;
|
|
84
|
-
if (allowedRedirectUris && allowedRedirectUris.length > 0) {
|
|
85
|
-
if (!isValidRedirectUri(finalRedirectUri, allowedRedirectUris)) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
`@chemmangat/msal-next: redirectUri "${finalRedirectUri}" is not in the allowed list`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
const finalPostLogoutUri = postLogoutRedirectUri || finalRedirectUri;
|
|
91
|
-
if (!isValidRedirectUri(finalPostLogoutUri, allowedRedirectUris)) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`@chemmangat/msal-next: postLogoutRedirectUri "${finalPostLogoutUri}" is not in the allowed list`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const msalConfig = {
|
|
98
|
-
auth: {
|
|
99
|
-
clientId,
|
|
100
|
-
authority: getAuthority(),
|
|
101
|
-
redirectUri: finalRedirectUri,
|
|
102
|
-
postLogoutRedirectUri: postLogoutRedirectUri || finalRedirectUri,
|
|
103
|
-
navigateToLoginRequestUrl
|
|
104
|
-
},
|
|
105
|
-
cache: {
|
|
106
|
-
cacheLocation,
|
|
107
|
-
storeAuthStateInCookie
|
|
108
|
-
},
|
|
109
|
-
system: {
|
|
110
|
-
loggerOptions: {
|
|
111
|
-
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
112
|
-
if (containsPii || !enableLogging) return;
|
|
113
|
-
switch (level) {
|
|
114
|
-
case msalBrowser.LogLevel.Error:
|
|
115
|
-
console.error("[MSAL]", message);
|
|
116
|
-
break;
|
|
117
|
-
case msalBrowser.LogLevel.Warning:
|
|
118
|
-
console.warn("[MSAL]", message);
|
|
119
|
-
break;
|
|
120
|
-
case msalBrowser.LogLevel.Info:
|
|
121
|
-
console.info("[MSAL]", message);
|
|
122
|
-
break;
|
|
123
|
-
case msalBrowser.LogLevel.Verbose:
|
|
124
|
-
console.debug("[MSAL]", message);
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
}),
|
|
128
|
-
logLevel: enableLogging ? msalBrowser.LogLevel.Verbose : msalBrowser.LogLevel.Error
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
return msalConfig;
|
|
133
|
-
}
|
|
134
|
-
var globalMsalInstance = null;
|
|
135
|
-
function getMsalInstance() {
|
|
136
|
-
return globalMsalInstance;
|
|
137
|
-
}
|
|
138
|
-
function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
|
|
139
|
-
const [msalInstance, setMsalInstance] = react.useState(null);
|
|
140
|
-
const instanceRef = react.useRef(null);
|
|
141
|
-
react.useEffect(() => {
|
|
142
|
-
if (typeof window === "undefined") {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (instanceRef.current) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const initializeMsal = async () => {
|
|
149
|
-
try {
|
|
150
|
-
const msalConfig = createMsalConfig(config);
|
|
151
|
-
const instance = new msalBrowser.PublicClientApplication(msalConfig);
|
|
152
|
-
await instance.initialize();
|
|
153
|
-
const response = await instance.handleRedirectPromise();
|
|
154
|
-
if (response && config.enableLogging) {
|
|
155
|
-
console.log("[MSAL] Redirect authentication successful");
|
|
156
|
-
}
|
|
157
|
-
const enableLogging = config.enableLogging || false;
|
|
158
|
-
instance.addEventCallback((event) => {
|
|
159
|
-
if (event.eventType === msalBrowser.EventType.LOGIN_SUCCESS) {
|
|
160
|
-
if (enableLogging) {
|
|
161
|
-
const payload = event.payload;
|
|
162
|
-
console.log("[MSAL] Login successful:", payload.account?.username);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (event.eventType === msalBrowser.EventType.LOGIN_FAILURE) {
|
|
166
|
-
console.error("[MSAL] Login failed:", event.error);
|
|
167
|
-
}
|
|
168
|
-
if (event.eventType === msalBrowser.EventType.LOGOUT_SUCCESS) {
|
|
169
|
-
if (enableLogging) {
|
|
170
|
-
console.log("[MSAL] Logout successful");
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
instanceRef.current = instance;
|
|
175
|
-
globalMsalInstance = instance;
|
|
176
|
-
setMsalInstance(instance);
|
|
177
|
-
if (onInitialized) {
|
|
178
|
-
onInitialized(instance);
|
|
179
|
-
}
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.error("[MSAL] Initialization failed:", error);
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
initializeMsal();
|
|
186
|
-
}, []);
|
|
187
|
-
if (typeof window === "undefined") {
|
|
188
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading authentication..." }) });
|
|
189
|
-
}
|
|
190
|
-
if (!msalInstance) {
|
|
191
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading authentication..." }) });
|
|
192
|
-
}
|
|
193
|
-
return /* @__PURE__ */ jsxRuntime.jsx(msalReact.MsalProvider, { instance: msalInstance, children });
|
|
194
|
-
}
|
|
195
|
-
var pendingTokenRequests = /* @__PURE__ */ new Map();
|
|
196
|
-
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
197
|
-
const { instance, accounts, inProgress } = msalReact.useMsal();
|
|
198
|
-
const account = msalReact.useAccount(accounts[0] || null);
|
|
199
|
-
const popupInProgressRef = react.useRef(false);
|
|
200
|
-
const isAuthenticated = react.useMemo(() => accounts.length > 0, [accounts]);
|
|
201
|
-
const loginPopup = react.useCallback(
|
|
202
|
-
async (scopes = defaultScopes) => {
|
|
203
|
-
try {
|
|
204
|
-
const request = {
|
|
205
|
-
scopes,
|
|
206
|
-
prompt: "select_account"
|
|
207
|
-
};
|
|
208
|
-
await instance.loginPopup(request);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
console.error("[MSAL] Login popup failed:", error);
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
[instance, defaultScopes]
|
|
215
|
-
);
|
|
216
|
-
const loginRedirect = react.useCallback(
|
|
217
|
-
async (scopes = defaultScopes) => {
|
|
218
|
-
try {
|
|
219
|
-
const request = {
|
|
220
|
-
scopes,
|
|
221
|
-
prompt: "select_account"
|
|
222
|
-
};
|
|
223
|
-
await instance.loginRedirect(request);
|
|
224
|
-
} catch (error) {
|
|
225
|
-
console.error("[MSAL] Login redirect failed:", error);
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
[instance, defaultScopes]
|
|
230
|
-
);
|
|
231
|
-
const logoutPopup = react.useCallback(async () => {
|
|
232
|
-
try {
|
|
233
|
-
await instance.logoutPopup({
|
|
234
|
-
account: account || void 0
|
|
235
|
-
});
|
|
236
|
-
} catch (error) {
|
|
237
|
-
console.error("[MSAL] Logout popup failed:", error);
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}, [instance, account]);
|
|
241
|
-
const logoutRedirect = react.useCallback(async () => {
|
|
242
|
-
try {
|
|
243
|
-
await instance.logoutRedirect({
|
|
244
|
-
account: account || void 0
|
|
245
|
-
});
|
|
246
|
-
} catch (error) {
|
|
247
|
-
console.error("[MSAL] Logout redirect failed:", error);
|
|
248
|
-
throw error;
|
|
249
|
-
}
|
|
250
|
-
}, [instance, account]);
|
|
251
|
-
const acquireTokenSilent = react.useCallback(
|
|
252
|
-
async (scopes = defaultScopes) => {
|
|
253
|
-
if (!account) {
|
|
254
|
-
throw new Error("[MSAL] No active account. Please login first.");
|
|
255
|
-
}
|
|
256
|
-
try {
|
|
257
|
-
const request = {
|
|
258
|
-
scopes,
|
|
259
|
-
account,
|
|
260
|
-
forceRefresh: false
|
|
261
|
-
};
|
|
262
|
-
const response = await instance.acquireTokenSilent(request);
|
|
263
|
-
return response.accessToken;
|
|
264
|
-
} catch (error) {
|
|
265
|
-
console.error("[MSAL] Silent token acquisition failed:", error);
|
|
266
|
-
throw error;
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
[instance, account, defaultScopes]
|
|
270
|
-
);
|
|
271
|
-
const acquireTokenPopup = react.useCallback(
|
|
272
|
-
async (scopes = defaultScopes) => {
|
|
273
|
-
if (!account) {
|
|
274
|
-
throw new Error("[MSAL] No active account. Please login first.");
|
|
275
|
-
}
|
|
276
|
-
if (popupInProgressRef.current) {
|
|
277
|
-
throw new Error("[MSAL] Popup already in progress. Please wait.");
|
|
278
|
-
}
|
|
279
|
-
try {
|
|
280
|
-
popupInProgressRef.current = true;
|
|
281
|
-
const request = {
|
|
282
|
-
scopes,
|
|
283
|
-
account
|
|
284
|
-
};
|
|
285
|
-
const response = await instance.acquireTokenPopup(request);
|
|
286
|
-
return response.accessToken;
|
|
287
|
-
} catch (error) {
|
|
288
|
-
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
289
|
-
throw error;
|
|
290
|
-
} finally {
|
|
291
|
-
popupInProgressRef.current = false;
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
[instance, account, defaultScopes]
|
|
295
|
-
);
|
|
296
|
-
const acquireTokenRedirect = react.useCallback(
|
|
297
|
-
async (scopes = defaultScopes) => {
|
|
298
|
-
if (!account) {
|
|
299
|
-
throw new Error("[MSAL] No active account. Please login first.");
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
const request = {
|
|
303
|
-
scopes,
|
|
304
|
-
account
|
|
305
|
-
};
|
|
306
|
-
await instance.acquireTokenRedirect(request);
|
|
307
|
-
} catch (error) {
|
|
308
|
-
console.error("[MSAL] Token redirect acquisition failed:", error);
|
|
309
|
-
throw error;
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
[instance, account, defaultScopes]
|
|
313
|
-
);
|
|
314
|
-
const acquireToken = react.useCallback(
|
|
315
|
-
async (scopes = defaultScopes) => {
|
|
316
|
-
const requestKey = `${account?.homeAccountId || "anonymous"}-${scopes.sort().join(",")}`;
|
|
317
|
-
const pendingRequest = pendingTokenRequests.get(requestKey);
|
|
318
|
-
if (pendingRequest) {
|
|
319
|
-
return pendingRequest;
|
|
320
|
-
}
|
|
321
|
-
const tokenRequest = (async () => {
|
|
322
|
-
try {
|
|
323
|
-
return await acquireTokenSilent(scopes);
|
|
324
|
-
} catch (error) {
|
|
325
|
-
console.warn("[MSAL] Silent token acquisition failed, falling back to popup");
|
|
326
|
-
return await acquireTokenPopup(scopes);
|
|
327
|
-
} finally {
|
|
328
|
-
pendingTokenRequests.delete(requestKey);
|
|
329
|
-
}
|
|
330
|
-
})();
|
|
331
|
-
pendingTokenRequests.set(requestKey, tokenRequest);
|
|
332
|
-
return tokenRequest;
|
|
333
|
-
},
|
|
334
|
-
[acquireTokenSilent, acquireTokenPopup, defaultScopes, account]
|
|
335
|
-
);
|
|
336
|
-
const clearSession = react.useCallback(async () => {
|
|
337
|
-
instance.setActiveAccount(null);
|
|
338
|
-
await instance.clearCache();
|
|
339
|
-
}, [instance]);
|
|
340
|
-
return {
|
|
341
|
-
account,
|
|
342
|
-
accounts,
|
|
343
|
-
isAuthenticated,
|
|
344
|
-
inProgress: inProgress !== msalBrowser.InteractionStatus.None,
|
|
345
|
-
loginPopup,
|
|
346
|
-
loginRedirect,
|
|
347
|
-
logoutPopup,
|
|
348
|
-
logoutRedirect,
|
|
349
|
-
acquireToken,
|
|
350
|
-
acquireTokenSilent,
|
|
351
|
-
acquireTokenPopup,
|
|
352
|
-
acquireTokenRedirect,
|
|
353
|
-
clearSession
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
function MicrosoftSignInButton({
|
|
357
|
-
text = "Sign in with Microsoft",
|
|
358
|
-
variant = "dark",
|
|
359
|
-
size = "medium",
|
|
360
|
-
useRedirect = false,
|
|
361
|
-
scopes,
|
|
362
|
-
className = "",
|
|
363
|
-
style,
|
|
364
|
-
onSuccess,
|
|
365
|
-
onError
|
|
366
|
-
}) {
|
|
367
|
-
const { loginPopup, loginRedirect, inProgress } = useMsalAuth();
|
|
368
|
-
const handleClick = async () => {
|
|
369
|
-
try {
|
|
370
|
-
if (useRedirect) {
|
|
371
|
-
await loginRedirect(scopes);
|
|
372
|
-
} else {
|
|
373
|
-
await loginPopup(scopes);
|
|
374
|
-
}
|
|
375
|
-
onSuccess?.();
|
|
376
|
-
} catch (error) {
|
|
377
|
-
onError?.(error);
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
const sizeStyles = {
|
|
381
|
-
small: {
|
|
382
|
-
padding: "8px 16px",
|
|
383
|
-
fontSize: "14px",
|
|
384
|
-
height: "36px"
|
|
385
|
-
},
|
|
386
|
-
medium: {
|
|
387
|
-
padding: "10px 20px",
|
|
388
|
-
fontSize: "15px",
|
|
389
|
-
height: "41px"
|
|
390
|
-
},
|
|
391
|
-
large: {
|
|
392
|
-
padding: "12px 24px",
|
|
393
|
-
fontSize: "16px",
|
|
394
|
-
height: "48px"
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
const variantStyles = {
|
|
398
|
-
dark: {
|
|
399
|
-
backgroundColor: "#2F2F2F",
|
|
400
|
-
color: "#FFFFFF",
|
|
401
|
-
border: "1px solid #8C8C8C"
|
|
402
|
-
},
|
|
403
|
-
light: {
|
|
404
|
-
backgroundColor: "#FFFFFF",
|
|
405
|
-
color: "#5E5E5E",
|
|
406
|
-
border: "1px solid #8C8C8C"
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
const baseStyles = {
|
|
410
|
-
display: "inline-flex",
|
|
411
|
-
alignItems: "center",
|
|
412
|
-
justifyContent: "center",
|
|
413
|
-
gap: "12px",
|
|
414
|
-
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',
|
|
415
|
-
fontWeight: 600,
|
|
416
|
-
borderRadius: "2px",
|
|
417
|
-
cursor: inProgress ? "not-allowed" : "pointer",
|
|
418
|
-
transition: "all 0.2s ease",
|
|
419
|
-
opacity: inProgress ? 0.6 : 1,
|
|
420
|
-
...variantStyles[variant],
|
|
421
|
-
...sizeStyles[size],
|
|
422
|
-
...style
|
|
423
|
-
};
|
|
424
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
425
|
-
"button",
|
|
426
|
-
{
|
|
427
|
-
onClick: handleClick,
|
|
428
|
-
disabled: inProgress,
|
|
429
|
-
className,
|
|
430
|
-
style: baseStyles,
|
|
431
|
-
"aria-label": text,
|
|
432
|
-
children: [
|
|
433
|
-
/* @__PURE__ */ jsxRuntime.jsx(MicrosoftLogo, {}),
|
|
434
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: text })
|
|
435
|
-
]
|
|
436
|
-
}
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
function MicrosoftLogo() {
|
|
440
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "21", height: "21", viewBox: "0 0 21 21", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
441
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "10", height: "10", fill: "#F25022" }),
|
|
442
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", width: "10", height: "10", fill: "#7FBA00" }),
|
|
443
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { y: "11", width: "10", height: "10", fill: "#00A4EF" }),
|
|
444
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", y: "11", width: "10", height: "10", fill: "#FFB900" })
|
|
445
|
-
] });
|
|
446
|
-
}
|
|
447
|
-
function SignOutButton({
|
|
448
|
-
text = "Sign out",
|
|
449
|
-
variant = "dark",
|
|
450
|
-
size = "medium",
|
|
451
|
-
useRedirect = false,
|
|
452
|
-
className = "",
|
|
453
|
-
style,
|
|
454
|
-
onSuccess,
|
|
455
|
-
onError
|
|
456
|
-
}) {
|
|
457
|
-
const { logoutPopup, logoutRedirect, inProgress } = useMsalAuth();
|
|
458
|
-
const handleClick = async () => {
|
|
459
|
-
try {
|
|
460
|
-
if (useRedirect) {
|
|
461
|
-
await logoutRedirect();
|
|
462
|
-
} else {
|
|
463
|
-
await logoutPopup();
|
|
464
|
-
}
|
|
465
|
-
onSuccess?.();
|
|
466
|
-
} catch (error) {
|
|
467
|
-
onError?.(error);
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
const sizeStyles = {
|
|
471
|
-
small: {
|
|
472
|
-
padding: "8px 16px",
|
|
473
|
-
fontSize: "14px",
|
|
474
|
-
height: "36px"
|
|
475
|
-
},
|
|
476
|
-
medium: {
|
|
477
|
-
padding: "10px 20px",
|
|
478
|
-
fontSize: "15px",
|
|
479
|
-
height: "41px"
|
|
480
|
-
},
|
|
481
|
-
large: {
|
|
482
|
-
padding: "12px 24px",
|
|
483
|
-
fontSize: "16px",
|
|
484
|
-
height: "48px"
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
const variantStyles = {
|
|
488
|
-
dark: {
|
|
489
|
-
backgroundColor: "#2F2F2F",
|
|
490
|
-
color: "#FFFFFF",
|
|
491
|
-
border: "1px solid #8C8C8C"
|
|
492
|
-
},
|
|
493
|
-
light: {
|
|
494
|
-
backgroundColor: "#FFFFFF",
|
|
495
|
-
color: "#5E5E5E",
|
|
496
|
-
border: "1px solid #8C8C8C"
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
const baseStyles = {
|
|
500
|
-
display: "inline-flex",
|
|
501
|
-
alignItems: "center",
|
|
502
|
-
justifyContent: "center",
|
|
503
|
-
gap: "12px",
|
|
504
|
-
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',
|
|
505
|
-
fontWeight: 600,
|
|
506
|
-
borderRadius: "2px",
|
|
507
|
-
cursor: inProgress ? "not-allowed" : "pointer",
|
|
508
|
-
transition: "all 0.2s ease",
|
|
509
|
-
opacity: inProgress ? 0.6 : 1,
|
|
510
|
-
...variantStyles[variant],
|
|
511
|
-
...sizeStyles[size],
|
|
512
|
-
...style
|
|
513
|
-
};
|
|
514
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
515
|
-
"button",
|
|
516
|
-
{
|
|
517
|
-
onClick: handleClick,
|
|
518
|
-
disabled: inProgress,
|
|
519
|
-
className,
|
|
520
|
-
style: baseStyles,
|
|
521
|
-
"aria-label": text,
|
|
522
|
-
children: [
|
|
523
|
-
/* @__PURE__ */ jsxRuntime.jsx(MicrosoftLogo2, {}),
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: text })
|
|
525
|
-
]
|
|
526
|
-
}
|
|
527
|
-
);
|
|
528
|
-
}
|
|
529
|
-
function MicrosoftLogo2() {
|
|
530
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "21", height: "21", viewBox: "0 0 21 21", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
531
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "10", height: "10", fill: "#F25022" }),
|
|
532
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", width: "10", height: "10", fill: "#7FBA00" }),
|
|
533
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { y: "11", width: "10", height: "10", fill: "#00A4EF" }),
|
|
534
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", y: "11", width: "10", height: "10", fill: "#FFB900" })
|
|
535
|
-
] });
|
|
536
|
-
}
|
|
537
|
-
function useGraphApi() {
|
|
538
|
-
const { acquireToken } = useMsalAuth();
|
|
539
|
-
const request = react.useCallback(
|
|
540
|
-
async (endpoint, options = {}) => {
|
|
541
|
-
const {
|
|
542
|
-
scopes = ["User.Read"],
|
|
543
|
-
version = "v1.0",
|
|
544
|
-
debug = false,
|
|
545
|
-
...fetchOptions
|
|
546
|
-
} = options;
|
|
547
|
-
try {
|
|
548
|
-
const token = await acquireToken(scopes);
|
|
549
|
-
const baseUrl = `https://graph.microsoft.com/${version}`;
|
|
550
|
-
const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
|
|
551
|
-
if (debug) {
|
|
552
|
-
console.log("[GraphAPI] Request:", { url, method: fetchOptions.method || "GET" });
|
|
553
|
-
}
|
|
554
|
-
const response = await fetch(url, {
|
|
555
|
-
...fetchOptions,
|
|
556
|
-
headers: {
|
|
557
|
-
"Authorization": `Bearer ${token}`,
|
|
558
|
-
"Content-Type": "application/json",
|
|
559
|
-
...fetchOptions.headers
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
if (!response.ok) {
|
|
563
|
-
const errorText = await response.text();
|
|
564
|
-
const errorMessage = `Graph API error (${response.status}): ${errorText}`;
|
|
565
|
-
throw new Error(errorMessage);
|
|
566
|
-
}
|
|
567
|
-
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
const data = await response.json();
|
|
571
|
-
if (debug) {
|
|
572
|
-
console.log("[GraphAPI] Response:", data);
|
|
573
|
-
}
|
|
574
|
-
return data;
|
|
575
|
-
} catch (error) {
|
|
576
|
-
const sanitizedMessage = sanitizeError(error);
|
|
577
|
-
console.error("[GraphAPI] Request failed:", sanitizedMessage);
|
|
578
|
-
throw new Error(sanitizedMessage);
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
[acquireToken]
|
|
582
|
-
);
|
|
583
|
-
const get = react.useCallback(
|
|
584
|
-
(endpoint, options = {}) => {
|
|
585
|
-
return request(endpoint, { ...options, method: "GET" });
|
|
586
|
-
},
|
|
587
|
-
[request]
|
|
588
|
-
);
|
|
589
|
-
const post = react.useCallback(
|
|
590
|
-
(endpoint, body, options = {}) => {
|
|
591
|
-
return request(endpoint, {
|
|
592
|
-
...options,
|
|
593
|
-
method: "POST",
|
|
594
|
-
body: body ? JSON.stringify(body) : void 0
|
|
595
|
-
});
|
|
596
|
-
},
|
|
597
|
-
[request]
|
|
598
|
-
);
|
|
599
|
-
const put = react.useCallback(
|
|
600
|
-
(endpoint, body, options = {}) => {
|
|
601
|
-
return request(endpoint, {
|
|
602
|
-
...options,
|
|
603
|
-
method: "PUT",
|
|
604
|
-
body: body ? JSON.stringify(body) : void 0
|
|
605
|
-
});
|
|
606
|
-
},
|
|
607
|
-
[request]
|
|
608
|
-
);
|
|
609
|
-
const patch = react.useCallback(
|
|
610
|
-
(endpoint, body, options = {}) => {
|
|
611
|
-
return request(endpoint, {
|
|
612
|
-
...options,
|
|
613
|
-
method: "PATCH",
|
|
614
|
-
body: body ? JSON.stringify(body) : void 0
|
|
615
|
-
});
|
|
616
|
-
},
|
|
617
|
-
[request]
|
|
618
|
-
);
|
|
619
|
-
const deleteRequest = react.useCallback(
|
|
620
|
-
(endpoint, options = {}) => {
|
|
621
|
-
return request(endpoint, { ...options, method: "DELETE" });
|
|
622
|
-
},
|
|
623
|
-
[request]
|
|
624
|
-
);
|
|
625
|
-
return {
|
|
626
|
-
get,
|
|
627
|
-
post,
|
|
628
|
-
put,
|
|
629
|
-
patch,
|
|
630
|
-
delete: deleteRequest,
|
|
631
|
-
request
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// src/hooks/useUserProfile.ts
|
|
636
|
-
var profileCache = /* @__PURE__ */ new Map();
|
|
637
|
-
var CACHE_DURATION = 5 * 60 * 1e3;
|
|
638
|
-
var MAX_CACHE_SIZE = 100;
|
|
639
|
-
function enforceCacheLimit() {
|
|
640
|
-
if (profileCache.size > MAX_CACHE_SIZE) {
|
|
641
|
-
const entries = Array.from(profileCache.entries());
|
|
642
|
-
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
643
|
-
const toRemove = entries.slice(0, profileCache.size - MAX_CACHE_SIZE);
|
|
644
|
-
toRemove.forEach(([key]) => {
|
|
645
|
-
const cached = profileCache.get(key);
|
|
646
|
-
if (cached?.data.photo) {
|
|
647
|
-
URL.revokeObjectURL(cached.data.photo);
|
|
648
|
-
}
|
|
649
|
-
profileCache.delete(key);
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
function useUserProfile() {
|
|
654
|
-
const { isAuthenticated, account } = useMsalAuth();
|
|
655
|
-
const graph = useGraphApi();
|
|
656
|
-
const [profile, setProfile] = react.useState(null);
|
|
657
|
-
const [loading, setLoading] = react.useState(false);
|
|
658
|
-
const [error, setError] = react.useState(null);
|
|
659
|
-
const fetchProfile = react.useCallback(async () => {
|
|
660
|
-
if (!isAuthenticated || !account) {
|
|
661
|
-
setProfile(null);
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
const cacheKey = account.homeAccountId;
|
|
665
|
-
const cached = profileCache.get(cacheKey);
|
|
666
|
-
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
|
|
667
|
-
setProfile(cached.data);
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
setLoading(true);
|
|
671
|
-
setError(null);
|
|
672
|
-
try {
|
|
673
|
-
const userData = await graph.get("/me", {
|
|
674
|
-
scopes: ["User.Read"]
|
|
675
|
-
});
|
|
676
|
-
let photoUrl;
|
|
677
|
-
try {
|
|
678
|
-
const photoBlob = await graph.get("/me/photo/$value", {
|
|
679
|
-
scopes: ["User.Read"],
|
|
680
|
-
headers: {
|
|
681
|
-
"Content-Type": "image/jpeg"
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
if (photoBlob) {
|
|
685
|
-
photoUrl = URL.createObjectURL(photoBlob);
|
|
686
|
-
}
|
|
687
|
-
} catch (photoError) {
|
|
688
|
-
console.debug("[UserProfile] Photo not available");
|
|
689
|
-
}
|
|
690
|
-
const profileData = {
|
|
691
|
-
id: userData.id,
|
|
692
|
-
displayName: userData.displayName,
|
|
693
|
-
givenName: userData.givenName,
|
|
694
|
-
surname: userData.surname,
|
|
695
|
-
userPrincipalName: userData.userPrincipalName,
|
|
696
|
-
mail: userData.mail,
|
|
697
|
-
jobTitle: userData.jobTitle,
|
|
698
|
-
officeLocation: userData.officeLocation,
|
|
699
|
-
mobilePhone: userData.mobilePhone,
|
|
700
|
-
businessPhones: userData.businessPhones,
|
|
701
|
-
photo: photoUrl
|
|
702
|
-
};
|
|
703
|
-
profileCache.set(cacheKey, {
|
|
704
|
-
data: profileData,
|
|
705
|
-
timestamp: Date.now()
|
|
706
|
-
});
|
|
707
|
-
enforceCacheLimit();
|
|
708
|
-
setProfile(profileData);
|
|
709
|
-
} catch (err) {
|
|
710
|
-
const error2 = err;
|
|
711
|
-
const sanitizedMessage = sanitizeError(error2);
|
|
712
|
-
const sanitizedError = new Error(sanitizedMessage);
|
|
713
|
-
setError(sanitizedError);
|
|
714
|
-
console.error("[UserProfile] Failed to fetch profile:", sanitizedMessage);
|
|
715
|
-
} finally {
|
|
716
|
-
setLoading(false);
|
|
717
|
-
}
|
|
718
|
-
}, [isAuthenticated, account, graph]);
|
|
719
|
-
const clearCache = react.useCallback(() => {
|
|
720
|
-
if (account) {
|
|
721
|
-
const cached = profileCache.get(account.homeAccountId);
|
|
722
|
-
if (cached?.data.photo) {
|
|
723
|
-
URL.revokeObjectURL(cached.data.photo);
|
|
724
|
-
}
|
|
725
|
-
profileCache.delete(account.homeAccountId);
|
|
726
|
-
}
|
|
727
|
-
if (profile?.photo) {
|
|
728
|
-
URL.revokeObjectURL(profile.photo);
|
|
729
|
-
}
|
|
730
|
-
setProfile(null);
|
|
731
|
-
}, [account, profile]);
|
|
732
|
-
react.useEffect(() => {
|
|
733
|
-
fetchProfile();
|
|
734
|
-
return () => {
|
|
735
|
-
if (profile?.photo) {
|
|
736
|
-
URL.revokeObjectURL(profile.photo);
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
}, [fetchProfile]);
|
|
740
|
-
react.useEffect(() => {
|
|
741
|
-
return () => {
|
|
742
|
-
if (profile?.photo) {
|
|
743
|
-
URL.revokeObjectURL(profile.photo);
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
}, [profile?.photo]);
|
|
747
|
-
return {
|
|
748
|
-
profile,
|
|
749
|
-
loading,
|
|
750
|
-
error,
|
|
751
|
-
refetch: fetchProfile,
|
|
752
|
-
clearCache
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
function UserAvatar({
|
|
756
|
-
size = 40,
|
|
757
|
-
className = "",
|
|
758
|
-
style,
|
|
759
|
-
showTooltip = true,
|
|
760
|
-
fallbackImage
|
|
761
|
-
}) {
|
|
762
|
-
const { profile, loading } = useUserProfile();
|
|
763
|
-
const [photoUrl, setPhotoUrl] = react.useState(null);
|
|
764
|
-
const [photoError, setPhotoError] = react.useState(false);
|
|
765
|
-
react.useEffect(() => {
|
|
766
|
-
if (profile?.photo) {
|
|
767
|
-
setPhotoUrl(profile.photo);
|
|
768
|
-
}
|
|
769
|
-
}, [profile?.photo]);
|
|
770
|
-
const getInitials = () => {
|
|
771
|
-
if (!profile) return "?";
|
|
772
|
-
const { givenName, surname, displayName: displayName2 } = profile;
|
|
773
|
-
if (givenName && surname) {
|
|
774
|
-
return `${givenName[0]}${surname[0]}`.toUpperCase();
|
|
775
|
-
}
|
|
776
|
-
if (displayName2) {
|
|
777
|
-
const parts = displayName2.split(" ");
|
|
778
|
-
if (parts.length >= 2) {
|
|
779
|
-
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
780
|
-
}
|
|
781
|
-
return displayName2.substring(0, 2).toUpperCase();
|
|
782
|
-
}
|
|
783
|
-
return "?";
|
|
784
|
-
};
|
|
785
|
-
const baseStyles = {
|
|
786
|
-
width: `${size}px`,
|
|
787
|
-
height: `${size}px`,
|
|
788
|
-
borderRadius: "50%",
|
|
789
|
-
display: "inline-flex",
|
|
790
|
-
alignItems: "center",
|
|
791
|
-
justifyContent: "center",
|
|
792
|
-
fontSize: `${size * 0.4}px`,
|
|
793
|
-
fontWeight: 600,
|
|
794
|
-
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',
|
|
795
|
-
backgroundColor: "#0078D4",
|
|
796
|
-
color: "#FFFFFF",
|
|
797
|
-
overflow: "hidden",
|
|
798
|
-
userSelect: "none",
|
|
799
|
-
...style
|
|
800
|
-
};
|
|
801
|
-
const displayName = profile?.displayName || "User";
|
|
802
|
-
if (loading) {
|
|
803
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
804
|
-
"div",
|
|
805
|
-
{
|
|
806
|
-
className,
|
|
807
|
-
style: { ...baseStyles, backgroundColor: "#E1E1E1" },
|
|
808
|
-
"aria-label": "Loading user avatar",
|
|
809
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: `${size * 0.3}px` }, children: "..." })
|
|
810
|
-
}
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
if (photoUrl && !photoError) {
|
|
814
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
815
|
-
"div",
|
|
816
|
-
{
|
|
817
|
-
className,
|
|
818
|
-
style: baseStyles,
|
|
819
|
-
title: showTooltip ? displayName : void 0,
|
|
820
|
-
"aria-label": `${displayName} avatar`,
|
|
821
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
822
|
-
"img",
|
|
823
|
-
{
|
|
824
|
-
src: photoUrl,
|
|
825
|
-
alt: displayName,
|
|
826
|
-
style: { width: "100%", height: "100%", objectFit: "cover" },
|
|
827
|
-
onError: () => {
|
|
828
|
-
setPhotoError(true);
|
|
829
|
-
if (fallbackImage) {
|
|
830
|
-
setPhotoUrl(fallbackImage);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
)
|
|
835
|
-
}
|
|
836
|
-
);
|
|
837
|
-
}
|
|
838
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
839
|
-
"div",
|
|
840
|
-
{
|
|
841
|
-
className,
|
|
842
|
-
style: baseStyles,
|
|
843
|
-
title: showTooltip ? displayName : void 0,
|
|
844
|
-
"aria-label": `${displayName} avatar`,
|
|
845
|
-
children: getInitials()
|
|
846
|
-
}
|
|
847
|
-
);
|
|
848
|
-
}
|
|
849
|
-
function AuthStatus({
|
|
850
|
-
className = "",
|
|
851
|
-
style,
|
|
852
|
-
showDetails = false,
|
|
853
|
-
renderLoading,
|
|
854
|
-
renderAuthenticated,
|
|
855
|
-
renderUnauthenticated
|
|
856
|
-
}) {
|
|
857
|
-
const { isAuthenticated, inProgress, account } = useMsalAuth();
|
|
858
|
-
const baseStyles = {
|
|
859
|
-
display: "inline-flex",
|
|
860
|
-
alignItems: "center",
|
|
861
|
-
gap: "8px",
|
|
862
|
-
padding: "8px 12px",
|
|
863
|
-
borderRadius: "4px",
|
|
864
|
-
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',
|
|
865
|
-
fontSize: "14px",
|
|
866
|
-
fontWeight: 500,
|
|
867
|
-
...style
|
|
868
|
-
};
|
|
869
|
-
if (inProgress) {
|
|
870
|
-
if (renderLoading) {
|
|
871
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderLoading() });
|
|
872
|
-
}
|
|
873
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
874
|
-
"div",
|
|
875
|
-
{
|
|
876
|
-
className,
|
|
877
|
-
style: { ...baseStyles, backgroundColor: "#FFF4CE", color: "#8A6D3B" },
|
|
878
|
-
role: "status",
|
|
879
|
-
"aria-live": "polite",
|
|
880
|
-
children: [
|
|
881
|
-
/* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { color: "#FFA500" }),
|
|
882
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Loading..." })
|
|
883
|
-
]
|
|
884
|
-
}
|
|
885
|
-
);
|
|
886
|
-
}
|
|
887
|
-
if (isAuthenticated) {
|
|
888
|
-
const username = account?.username || account?.name || "User";
|
|
889
|
-
if (renderAuthenticated) {
|
|
890
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderAuthenticated(username) });
|
|
891
|
-
}
|
|
892
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
893
|
-
"div",
|
|
894
|
-
{
|
|
895
|
-
className,
|
|
896
|
-
style: { ...baseStyles, backgroundColor: "#D4EDDA", color: "#155724" },
|
|
897
|
-
role: "status",
|
|
898
|
-
"aria-live": "polite",
|
|
899
|
-
children: [
|
|
900
|
-
/* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { color: "#28A745" }),
|
|
901
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: showDetails ? `Authenticated as ${username}` : "Authenticated" })
|
|
902
|
-
]
|
|
903
|
-
}
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
if (renderUnauthenticated) {
|
|
907
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderUnauthenticated() });
|
|
908
|
-
}
|
|
909
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
910
|
-
"div",
|
|
911
|
-
{
|
|
912
|
-
className,
|
|
913
|
-
style: { ...baseStyles, backgroundColor: "#F8D7DA", color: "#721C24" },
|
|
914
|
-
role: "status",
|
|
915
|
-
"aria-live": "polite",
|
|
916
|
-
children: [
|
|
917
|
-
/* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { color: "#DC3545" }),
|
|
918
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Not authenticated" })
|
|
919
|
-
]
|
|
920
|
-
}
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
function StatusIndicator({ color }) {
|
|
924
|
-
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "4", r: "4", fill: color }) });
|
|
925
|
-
}
|
|
926
|
-
function AuthGuard({
|
|
927
|
-
children,
|
|
928
|
-
loadingComponent,
|
|
929
|
-
fallbackComponent,
|
|
930
|
-
useRedirect = true,
|
|
931
|
-
scopes,
|
|
932
|
-
onAuthRequired
|
|
933
|
-
}) {
|
|
934
|
-
const { isAuthenticated, inProgress, loginRedirect, loginPopup } = useMsalAuth();
|
|
935
|
-
react.useEffect(() => {
|
|
936
|
-
if (!isAuthenticated && !inProgress) {
|
|
937
|
-
onAuthRequired?.();
|
|
938
|
-
const login = async () => {
|
|
939
|
-
try {
|
|
940
|
-
if (useRedirect) {
|
|
941
|
-
await loginRedirect(scopes);
|
|
942
|
-
} else {
|
|
943
|
-
await loginPopup(scopes);
|
|
944
|
-
}
|
|
945
|
-
} catch (error) {
|
|
946
|
-
console.error("[AuthGuard] Authentication failed:", error);
|
|
947
|
-
}
|
|
948
|
-
};
|
|
949
|
-
login();
|
|
950
|
-
}
|
|
951
|
-
}, [isAuthenticated, inProgress, useRedirect, scopes, loginRedirect, loginPopup, onAuthRequired]);
|
|
952
|
-
if (inProgress) {
|
|
953
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Authenticating..." }) });
|
|
954
|
-
}
|
|
955
|
-
if (!isAuthenticated) {
|
|
956
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallbackComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Redirecting to login..." }) });
|
|
957
|
-
}
|
|
958
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
959
|
-
}
|
|
960
|
-
var ErrorBoundary = class extends react.Component {
|
|
961
|
-
constructor(props) {
|
|
962
|
-
super(props);
|
|
963
|
-
this.reset = () => {
|
|
964
|
-
this.setState({
|
|
965
|
-
hasError: false,
|
|
966
|
-
error: null
|
|
967
|
-
});
|
|
968
|
-
};
|
|
969
|
-
this.state = {
|
|
970
|
-
hasError: false,
|
|
971
|
-
error: null
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
static getDerivedStateFromError(error) {
|
|
975
|
-
return {
|
|
976
|
-
hasError: true,
|
|
977
|
-
error
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
componentDidCatch(error, errorInfo) {
|
|
981
|
-
const { onError, debug } = this.props;
|
|
982
|
-
if (debug) {
|
|
983
|
-
console.error("[ErrorBoundary] Caught error:", error);
|
|
984
|
-
console.error("[ErrorBoundary] Error info:", errorInfo);
|
|
985
|
-
}
|
|
986
|
-
onError?.(error, errorInfo);
|
|
987
|
-
}
|
|
988
|
-
render() {
|
|
989
|
-
const { hasError, error } = this.state;
|
|
990
|
-
const { children, fallback } = this.props;
|
|
991
|
-
if (hasError && error) {
|
|
992
|
-
if (fallback) {
|
|
993
|
-
return fallback(error, this.reset);
|
|
994
|
-
}
|
|
995
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
996
|
-
"div",
|
|
997
|
-
{
|
|
998
|
-
style: {
|
|
999
|
-
padding: "20px",
|
|
1000
|
-
margin: "20px",
|
|
1001
|
-
border: "1px solid #DC3545",
|
|
1002
|
-
borderRadius: "4px",
|
|
1003
|
-
backgroundColor: "#F8D7DA",
|
|
1004
|
-
color: "#721C24",
|
|
1005
|
-
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif'
|
|
1006
|
-
},
|
|
1007
|
-
children: [
|
|
1008
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: "0 0 10px 0", fontSize: "18px" }, children: "Authentication Error" }),
|
|
1009
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "0 0 10px 0" }, children: error.message }),
|
|
1010
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1011
|
-
"button",
|
|
1012
|
-
{
|
|
1013
|
-
onClick: this.reset,
|
|
1014
|
-
style: {
|
|
1015
|
-
padding: "8px 16px",
|
|
1016
|
-
backgroundColor: "#DC3545",
|
|
1017
|
-
color: "#FFFFFF",
|
|
1018
|
-
border: "none",
|
|
1019
|
-
borderRadius: "4px",
|
|
1020
|
-
cursor: "pointer",
|
|
1021
|
-
fontSize: "14px",
|
|
1022
|
-
fontWeight: 600
|
|
1023
|
-
},
|
|
1024
|
-
children: "Try Again"
|
|
1025
|
-
}
|
|
1026
|
-
)
|
|
1027
|
-
]
|
|
1028
|
-
}
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
return children;
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
|
-
var rolesCache = /* @__PURE__ */ new Map();
|
|
1035
|
-
var CACHE_DURATION2 = 5 * 60 * 1e3;
|
|
1036
|
-
var MAX_CACHE_SIZE2 = 100;
|
|
1037
|
-
function clearRolesCache(accountId) {
|
|
1038
|
-
if (accountId) {
|
|
1039
|
-
rolesCache.delete(accountId);
|
|
1040
|
-
} else {
|
|
1041
|
-
rolesCache.clear();
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
function enforceCacheLimit2() {
|
|
1045
|
-
if (rolesCache.size > MAX_CACHE_SIZE2) {
|
|
1046
|
-
const entries = Array.from(rolesCache.entries());
|
|
1047
|
-
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
1048
|
-
const toRemove = entries.slice(0, rolesCache.size - MAX_CACHE_SIZE2);
|
|
1049
|
-
toRemove.forEach(([key]) => rolesCache.delete(key));
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
function useRoles() {
|
|
1053
|
-
const { isAuthenticated, account } = useMsalAuth();
|
|
1054
|
-
const graph = useGraphApi();
|
|
1055
|
-
const [roles, setRoles] = react.useState([]);
|
|
1056
|
-
const [groups, setGroups] = react.useState([]);
|
|
1057
|
-
const [loading, setLoading] = react.useState(false);
|
|
1058
|
-
const [error, setError] = react.useState(null);
|
|
1059
|
-
const fetchRolesAndGroups = react.useCallback(async () => {
|
|
1060
|
-
if (!isAuthenticated || !account) {
|
|
1061
|
-
setRoles([]);
|
|
1062
|
-
setGroups([]);
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
const cacheKey = account.homeAccountId;
|
|
1066
|
-
const cached = rolesCache.get(cacheKey);
|
|
1067
|
-
if (cached && Date.now() - cached.timestamp < CACHE_DURATION2) {
|
|
1068
|
-
setRoles(cached.roles);
|
|
1069
|
-
setGroups(cached.groups);
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
setLoading(true);
|
|
1073
|
-
setError(null);
|
|
1074
|
-
try {
|
|
1075
|
-
const idTokenClaims = account.idTokenClaims;
|
|
1076
|
-
const tokenRoles = idTokenClaims?.roles || [];
|
|
1077
|
-
const groupsResponse = await graph.get("/me/memberOf", {
|
|
1078
|
-
scopes: ["User.Read", "Directory.Read.All"]
|
|
1079
|
-
});
|
|
1080
|
-
const userGroups = groupsResponse.value.map((group) => group.id);
|
|
1081
|
-
rolesCache.set(cacheKey, {
|
|
1082
|
-
roles: tokenRoles,
|
|
1083
|
-
groups: userGroups,
|
|
1084
|
-
timestamp: Date.now()
|
|
1085
|
-
});
|
|
1086
|
-
enforceCacheLimit2();
|
|
1087
|
-
setRoles(tokenRoles);
|
|
1088
|
-
setGroups(userGroups);
|
|
1089
|
-
} catch (err) {
|
|
1090
|
-
const error2 = err;
|
|
1091
|
-
const sanitizedMessage = sanitizeError(error2);
|
|
1092
|
-
const sanitizedError = new Error(sanitizedMessage);
|
|
1093
|
-
setError(sanitizedError);
|
|
1094
|
-
console.error("[Roles] Failed to fetch roles/groups:", sanitizedMessage);
|
|
1095
|
-
const idTokenClaims = account.idTokenClaims;
|
|
1096
|
-
const tokenRoles = idTokenClaims?.roles || [];
|
|
1097
|
-
setRoles(tokenRoles);
|
|
1098
|
-
} finally {
|
|
1099
|
-
setLoading(false);
|
|
1100
|
-
}
|
|
1101
|
-
}, [isAuthenticated, account, graph]);
|
|
1102
|
-
const hasRole = react.useCallback(
|
|
1103
|
-
(role) => {
|
|
1104
|
-
return roles.includes(role);
|
|
1105
|
-
},
|
|
1106
|
-
[roles]
|
|
1107
|
-
);
|
|
1108
|
-
const hasGroup = react.useCallback(
|
|
1109
|
-
(groupId) => {
|
|
1110
|
-
return groups.includes(groupId);
|
|
1111
|
-
},
|
|
1112
|
-
[groups]
|
|
1113
|
-
);
|
|
1114
|
-
const hasAnyRole = react.useCallback(
|
|
1115
|
-
(checkRoles) => {
|
|
1116
|
-
return checkRoles.some((role) => roles.includes(role));
|
|
1117
|
-
},
|
|
1118
|
-
[roles]
|
|
1119
|
-
);
|
|
1120
|
-
const hasAllRoles = react.useCallback(
|
|
1121
|
-
(checkRoles) => {
|
|
1122
|
-
return checkRoles.every((role) => roles.includes(role));
|
|
1123
|
-
},
|
|
1124
|
-
[roles]
|
|
1125
|
-
);
|
|
1126
|
-
react.useEffect(() => {
|
|
1127
|
-
fetchRolesAndGroups();
|
|
1128
|
-
return () => {
|
|
1129
|
-
if (account) {
|
|
1130
|
-
clearRolesCache(account.homeAccountId);
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
}, [fetchRolesAndGroups, account]);
|
|
1134
|
-
return {
|
|
1135
|
-
roles,
|
|
1136
|
-
groups,
|
|
1137
|
-
loading,
|
|
1138
|
-
error,
|
|
1139
|
-
hasRole,
|
|
1140
|
-
hasGroup,
|
|
1141
|
-
hasAnyRole,
|
|
1142
|
-
hasAllRoles,
|
|
1143
|
-
refetch: fetchRolesAndGroups
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
function withAuth(Component2, options = {}) {
|
|
1147
|
-
const { displayName, ...guardProps } = options;
|
|
1148
|
-
const WrappedComponent = (props) => {
|
|
1149
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AuthGuard, { ...guardProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
|
|
1150
|
-
};
|
|
1151
|
-
WrappedComponent.displayName = displayName || `withAuth(${Component2.displayName || Component2.name || "Component"})`;
|
|
1152
|
-
return WrappedComponent;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// src/utils/tokenRetry.ts
|
|
1156
|
-
async function retryWithBackoff(fn, config = {}) {
|
|
1157
|
-
const {
|
|
1158
|
-
maxRetries = 3,
|
|
1159
|
-
initialDelay = 1e3,
|
|
1160
|
-
maxDelay = 1e4,
|
|
1161
|
-
backoffMultiplier = 2,
|
|
1162
|
-
debug = false
|
|
1163
|
-
} = config;
|
|
1164
|
-
let lastError;
|
|
1165
|
-
let delay = initialDelay;
|
|
1166
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1167
|
-
try {
|
|
1168
|
-
if (debug && attempt > 0) {
|
|
1169
|
-
console.log(`[TokenRetry] Attempt ${attempt + 1}/${maxRetries + 1}`);
|
|
1170
|
-
}
|
|
1171
|
-
return await fn();
|
|
1172
|
-
} catch (error) {
|
|
1173
|
-
lastError = error;
|
|
1174
|
-
if (attempt === maxRetries) {
|
|
1175
|
-
if (debug) {
|
|
1176
|
-
console.error("[TokenRetry] All retry attempts failed");
|
|
1177
|
-
}
|
|
1178
|
-
break;
|
|
1179
|
-
}
|
|
1180
|
-
if (!isRetryableError(error)) {
|
|
1181
|
-
if (debug) {
|
|
1182
|
-
console.log("[TokenRetry] Non-retryable error, aborting");
|
|
1183
|
-
}
|
|
1184
|
-
throw error;
|
|
1185
|
-
}
|
|
1186
|
-
if (debug) {
|
|
1187
|
-
console.warn(`[TokenRetry] Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
|
|
1188
|
-
}
|
|
1189
|
-
await sleep(delay);
|
|
1190
|
-
delay = Math.min(delay * backoffMultiplier, maxDelay);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
throw lastError;
|
|
1194
|
-
}
|
|
1195
|
-
function isRetryableError(error) {
|
|
1196
|
-
const message = error.message.toLowerCase();
|
|
1197
|
-
if (message.includes("network") || message.includes("timeout") || message.includes("fetch") || message.includes("connection")) {
|
|
1198
|
-
return true;
|
|
1199
|
-
}
|
|
1200
|
-
if (message.includes("500") || message.includes("502") || message.includes("503")) {
|
|
1201
|
-
return true;
|
|
1202
|
-
}
|
|
1203
|
-
if (message.includes("429") || message.includes("rate limit")) {
|
|
1204
|
-
return true;
|
|
1205
|
-
}
|
|
1206
|
-
if (message.includes("token") && message.includes("expired")) {
|
|
1207
|
-
return true;
|
|
1208
|
-
}
|
|
1209
|
-
return false;
|
|
1210
|
-
}
|
|
1211
|
-
function sleep(ms) {
|
|
1212
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1213
|
-
}
|
|
1214
|
-
function createRetryWrapper(fn, config = {}) {
|
|
1215
|
-
return (...args) => {
|
|
1216
|
-
return retryWithBackoff(() => fn(...args), config);
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// src/utils/debugLogger.ts
|
|
1221
|
-
var DebugLogger = class {
|
|
1222
|
-
constructor(config = {}) {
|
|
1223
|
-
this.config = {
|
|
1224
|
-
enabled: config.enabled ?? false,
|
|
1225
|
-
prefix: config.prefix ?? "[MSAL-Next]",
|
|
1226
|
-
showTimestamp: config.showTimestamp ?? true,
|
|
1227
|
-
level: config.level ?? "info"
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
shouldLog(level) {
|
|
1231
|
-
if (!this.config.enabled) return false;
|
|
1232
|
-
const levels = ["error", "warn", "info", "debug"];
|
|
1233
|
-
const currentLevelIndex = levels.indexOf(this.config.level);
|
|
1234
|
-
const messageLevelIndex = levels.indexOf(level);
|
|
1235
|
-
return messageLevelIndex <= currentLevelIndex;
|
|
1236
|
-
}
|
|
1237
|
-
formatMessage(level, message, data) {
|
|
1238
|
-
const timestamp = this.config.showTimestamp ? `[${(/* @__PURE__ */ new Date()).toISOString()}]` : "";
|
|
1239
|
-
const prefix = this.config.prefix;
|
|
1240
|
-
const levelStr = `[${level.toUpperCase()}]`;
|
|
1241
|
-
let formatted = `${timestamp} ${prefix} ${levelStr} ${message}`;
|
|
1242
|
-
if (data !== void 0) {
|
|
1243
|
-
formatted += "\n" + JSON.stringify(data, null, 2);
|
|
1244
|
-
}
|
|
1245
|
-
return formatted;
|
|
1246
|
-
}
|
|
1247
|
-
error(message, data) {
|
|
1248
|
-
if (this.shouldLog("error")) {
|
|
1249
|
-
console.error(this.formatMessage("error", message, data));
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
warn(message, data) {
|
|
1253
|
-
if (this.shouldLog("warn")) {
|
|
1254
|
-
console.warn(this.formatMessage("warn", message, data));
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
info(message, data) {
|
|
1258
|
-
if (this.shouldLog("info")) {
|
|
1259
|
-
console.info(this.formatMessage("info", message, data));
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
debug(message, data) {
|
|
1263
|
-
if (this.shouldLog("debug")) {
|
|
1264
|
-
console.debug(this.formatMessage("debug", message, data));
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
group(label) {
|
|
1268
|
-
if (this.config.enabled) {
|
|
1269
|
-
console.group(`${this.config.prefix} ${label}`);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
groupEnd() {
|
|
1273
|
-
if (this.config.enabled) {
|
|
1274
|
-
console.groupEnd();
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
setEnabled(enabled) {
|
|
1278
|
-
this.config.enabled = enabled;
|
|
1279
|
-
}
|
|
1280
|
-
setLevel(level) {
|
|
1281
|
-
if (level) {
|
|
1282
|
-
this.config.level = level;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
};
|
|
1286
|
-
var globalLogger = null;
|
|
1287
|
-
function getDebugLogger(config) {
|
|
1288
|
-
if (!globalLogger) {
|
|
1289
|
-
globalLogger = new DebugLogger(config);
|
|
1290
|
-
} else if (config) {
|
|
1291
|
-
if (config.enabled !== void 0) {
|
|
1292
|
-
globalLogger.setEnabled(config.enabled);
|
|
1293
|
-
}
|
|
1294
|
-
if (config.level) {
|
|
1295
|
-
globalLogger.setLevel(config.level);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
return globalLogger;
|
|
1299
|
-
}
|
|
1300
|
-
function createScopedLogger(scope, config) {
|
|
1301
|
-
return new DebugLogger({
|
|
1302
|
-
...config,
|
|
1303
|
-
prefix: `[MSAL-Next:${scope}]`
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
function createAuthMiddleware(config = {}) {
|
|
1307
|
-
const {
|
|
1308
|
-
protectedRoutes = [],
|
|
1309
|
-
publicOnlyRoutes = [],
|
|
1310
|
-
loginPath = "/login",
|
|
1311
|
-
redirectAfterLogin = "/",
|
|
1312
|
-
sessionCookie = "msal.account",
|
|
1313
|
-
isAuthenticated: customAuthCheck,
|
|
1314
|
-
debug = false
|
|
1315
|
-
} = config;
|
|
1316
|
-
return async function authMiddleware(request) {
|
|
1317
|
-
const { pathname } = request.nextUrl;
|
|
1318
|
-
if (debug) {
|
|
1319
|
-
console.log("[AuthMiddleware] Processing:", pathname);
|
|
1320
|
-
}
|
|
1321
|
-
let authenticated = false;
|
|
1322
|
-
if (customAuthCheck) {
|
|
1323
|
-
authenticated = await customAuthCheck(request);
|
|
1324
|
-
} else {
|
|
1325
|
-
const sessionData = request.cookies.get(sessionCookie);
|
|
1326
|
-
authenticated = !!sessionData?.value;
|
|
1327
|
-
}
|
|
1328
|
-
if (debug) {
|
|
1329
|
-
console.log("[AuthMiddleware] Authenticated:", authenticated);
|
|
1330
|
-
}
|
|
1331
|
-
const isProtectedRoute = protectedRoutes.some(
|
|
1332
|
-
(route) => pathname.startsWith(route)
|
|
1333
|
-
);
|
|
1334
|
-
const isPublicOnlyRoute = publicOnlyRoutes.some(
|
|
1335
|
-
(route) => pathname.startsWith(route)
|
|
1336
|
-
);
|
|
1337
|
-
if (isProtectedRoute && !authenticated) {
|
|
1338
|
-
if (debug) {
|
|
1339
|
-
console.log("[AuthMiddleware] Redirecting to login");
|
|
1340
|
-
}
|
|
1341
|
-
const url = request.nextUrl.clone();
|
|
1342
|
-
url.pathname = loginPath;
|
|
1343
|
-
url.searchParams.set("returnUrl", pathname);
|
|
1344
|
-
return server.NextResponse.redirect(url);
|
|
1345
|
-
}
|
|
1346
|
-
if (isPublicOnlyRoute && authenticated) {
|
|
1347
|
-
if (debug) {
|
|
1348
|
-
console.log("[AuthMiddleware] Redirecting to home");
|
|
1349
|
-
}
|
|
1350
|
-
const returnUrl = request.nextUrl.searchParams.get("returnUrl");
|
|
1351
|
-
const url = request.nextUrl.clone();
|
|
1352
|
-
url.pathname = returnUrl || redirectAfterLogin;
|
|
1353
|
-
url.searchParams.delete("returnUrl");
|
|
1354
|
-
return server.NextResponse.redirect(url);
|
|
1355
|
-
}
|
|
1356
|
-
const response = server.NextResponse.next();
|
|
1357
|
-
if (authenticated) {
|
|
1358
|
-
response.headers.set("x-msal-authenticated", "true");
|
|
1359
|
-
try {
|
|
1360
|
-
const sessionData = request.cookies.get(sessionCookie);
|
|
1361
|
-
if (sessionData?.value) {
|
|
1362
|
-
const account = safeJsonParse(sessionData.value, isValidAccountData);
|
|
1363
|
-
if (account?.username) {
|
|
1364
|
-
response.headers.set("x-msal-username", account.username);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
} catch (error) {
|
|
1368
|
-
if (debug) {
|
|
1369
|
-
console.warn("[AuthMiddleware] Failed to parse session data");
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
return response;
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
Object.defineProperty(exports, "useAccount", {
|
|
1378
|
-
enumerable: true,
|
|
1379
|
-
get: function () { return msalReact.useAccount; }
|
|
1380
|
-
});
|
|
1381
|
-
Object.defineProperty(exports, "useIsAuthenticated", {
|
|
1382
|
-
enumerable: true,
|
|
1383
|
-
get: function () { return msalReact.useIsAuthenticated; }
|
|
1384
|
-
});
|
|
1385
|
-
Object.defineProperty(exports, "useMsal", {
|
|
1386
|
-
enumerable: true,
|
|
1387
|
-
get: function () { return msalReact.useMsal; }
|
|
1388
|
-
});
|
|
1389
|
-
exports.AuthGuard = AuthGuard;
|
|
1390
|
-
exports.AuthStatus = AuthStatus;
|
|
1391
|
-
exports.ErrorBoundary = ErrorBoundary;
|
|
1392
|
-
exports.MicrosoftSignInButton = MicrosoftSignInButton;
|
|
1393
|
-
exports.MsalAuthProvider = MsalAuthProvider;
|
|
1394
|
-
exports.SignOutButton = SignOutButton;
|
|
1395
|
-
exports.UserAvatar = UserAvatar;
|
|
1396
|
-
exports.createAuthMiddleware = createAuthMiddleware;
|
|
1397
|
-
exports.createMsalConfig = createMsalConfig;
|
|
1398
|
-
exports.createRetryWrapper = createRetryWrapper;
|
|
1399
|
-
exports.createScopedLogger = createScopedLogger;
|
|
1400
|
-
exports.getDebugLogger = getDebugLogger;
|
|
1401
|
-
exports.getMsalInstance = getMsalInstance;
|
|
1402
|
-
exports.isValidAccountData = isValidAccountData;
|
|
1403
|
-
exports.isValidRedirectUri = isValidRedirectUri;
|
|
1404
|
-
exports.isValidScope = isValidScope;
|
|
1405
|
-
exports.retryWithBackoff = retryWithBackoff;
|
|
1406
|
-
exports.safeJsonParse = safeJsonParse;
|
|
1407
|
-
exports.sanitizeError = sanitizeError;
|
|
1408
|
-
exports.useGraphApi = useGraphApi;
|
|
1409
|
-
exports.useMsalAuth = useMsalAuth;
|
|
1410
|
-
exports.useRoles = useRoles;
|
|
1411
|
-
exports.useUserProfile = useUserProfile;
|
|
1412
|
-
exports.validateScopes = validateScopes;
|
|
1413
|
-
exports.withAuth = withAuth;
|
|
1414
|
-
//# sourceMappingURL=index.js.map
|
|
1415
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
'use strict';var msalReact=require('@azure/msal-react'),msalBrowser=require('@azure/msal-browser'),react=require('react'),jsxRuntime=require('react/jsx-runtime'),server=require('next/server');function z(r,e){try{let t=JSON.parse(r);return e(t)?t:(console.warn("[Validation] JSON validation failed"),null)}catch(t){return console.error("[Validation] JSON parse error:",t),null}}function B(r){return typeof r=="object"&&r!==null&&typeof r.homeAccountId=="string"&&r.homeAccountId.length>0&&typeof r.username=="string"&&r.username.length>0&&(r.name===void 0||typeof r.name=="string")}function C(r){return r instanceof Error?r.message.replace(/[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/g,"[TOKEN_REDACTED]").replace(/[a-f0-9]{32,}/gi,"[SECRET_REDACTED]").replace(/Bearer\s+[^\s]+/gi,"Bearer [REDACTED]"):"An unexpected error occurred"}function G(r,e){try{let t=new URL(r);return e.some(o=>{let n=new URL(o);return t.origin===n.origin})}catch{return false}}function oe(r){return /^[a-zA-Z0-9._-]+$/.test(r)}function me(r){return Array.isArray(r)&&r.every(oe)}function V(r){if(r.msalConfig)return r.msalConfig;let{clientId:e,tenantId:t,authorityType:o="common",redirectUri:n,postLogoutRedirectUri:u,cacheLocation:d="sessionStorage",storeAuthStateInCookie:l=false,navigateToLoginRequestUrl:c=true,enableLogging:s=false,loggerCallback:h,allowedRedirectUris:p}=r;if(!e)throw new Error("@chemmangat/msal-next: clientId is required");let m=()=>{if(o==="tenant"){if(!t)throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is "tenant"');return `https://login.microsoftonline.com/${t}`}return `https://login.microsoftonline.com/${o}`},g=typeof window<"u"?window.location.origin:"http://localhost:3000",y=n||g;if(p&&p.length>0){if(!G(y,p))throw new Error(`@chemmangat/msal-next: redirectUri "${y}" is not in the allowed list`);let i=u||y;if(!G(i,p))throw new Error(`@chemmangat/msal-next: postLogoutRedirectUri "${i}" is not in the allowed list`)}return {auth:{clientId:e,authority:m(),redirectUri:y,postLogoutRedirectUri:u||y,navigateToLoginRequestUrl:c},cache:{cacheLocation:d,storeAuthStateInCookie:l},system:{loggerOptions:{loggerCallback:h||((i,a,b)=>{if(!(b||!s))switch(i){case msalBrowser.LogLevel.Error:console.error("[MSAL]",a);break;case msalBrowser.LogLevel.Warning:console.warn("[MSAL]",a);break;case msalBrowser.LogLevel.Info:console.info("[MSAL]",a);break;case msalBrowser.LogLevel.Verbose:console.debug("[MSAL]",a);break}}),logLevel:s?msalBrowser.LogLevel.Verbose:msalBrowser.LogLevel.Error}}}}var ie=null;function Re(){return ie}function ve({children:r,loadingComponent:e,onInitialized:t,...o}){let[n,u]=react.useState(null),d=react.useRef(null);return react.useEffect(()=>{if(typeof window>"u"||d.current)return;(async()=>{try{let c=V(o),s=new msalBrowser.PublicClientApplication(c);await s.initialize(),await s.handleRedirectPromise()&&o.enableLogging&&console.log("[MSAL] Redirect authentication successful");let p=o.enableLogging||!1;s.addEventCallback(m=>{if(m.eventType===msalBrowser.EventType.LOGIN_SUCCESS&&p){let g=m.payload;console.log("[MSAL] Login successful:",g.account?.username);}m.eventType===msalBrowser.EventType.LOGIN_FAILURE&&console.error("[MSAL] Login failed:",m.error),m.eventType===msalBrowser.EventType.LOGOUT_SUCCESS&&p&&console.log("[MSAL] Logout successful");}),d.current=s,ie=s,u(s),t&&t(s);}catch(c){throw console.error("[MSAL] Initialization failed:",c),c}})();},[]),typeof window>"u"?jsxRuntime.jsx(jsxRuntime.Fragment,{children:e||jsxRuntime.jsx("div",{children:"Loading authentication..."})}):n?jsxRuntime.jsx(msalReact.MsalProvider,{instance:n,children:r}):jsxRuntime.jsx(jsxRuntime.Fragment,{children:e||jsxRuntime.jsx("div",{children:"Loading authentication..."})})}var _=new Map;function A(r=["User.Read"]){let{instance:e,accounts:t,inProgress:o}=msalReact.useMsal(),n=msalReact.useAccount(t[0]||null),u=react.useRef(false),d=react.useMemo(()=>t.length>0,[t]),l=react.useCallback(async(i=r)=>{try{let a={scopes:i,prompt:"select_account"};await e.loginPopup(a);}catch(a){throw console.error("[MSAL] Login popup failed:",a),a}},[e,r]),c=react.useCallback(async(i=r)=>{try{let a={scopes:i,prompt:"select_account"};await e.loginRedirect(a);}catch(a){throw console.error("[MSAL] Login redirect failed:",a),a}},[e,r]),s=react.useCallback(async()=>{try{await e.logoutPopup({account:n||void 0});}catch(i){throw console.error("[MSAL] Logout popup failed:",i),i}},[e,n]),h=react.useCallback(async()=>{try{await e.logoutRedirect({account:n||void 0});}catch(i){throw console.error("[MSAL] Logout redirect failed:",i),i}},[e,n]),p=react.useCallback(async(i=r)=>{if(!n)throw new Error("[MSAL] No active account. Please login first.");try{let a={scopes:i,account:n,forceRefresh:!1};return (await e.acquireTokenSilent(a)).accessToken}catch(a){throw console.error("[MSAL] Silent token acquisition failed:",a),a}},[e,n,r]),m=react.useCallback(async(i=r)=>{if(!n)throw new Error("[MSAL] No active account. Please login first.");if(u.current)throw new Error("[MSAL] Popup already in progress. Please wait.");try{u.current=!0;let a={scopes:i,account:n};return (await e.acquireTokenPopup(a)).accessToken}catch(a){throw console.error("[MSAL] Token popup acquisition failed:",a),a}finally{u.current=false;}},[e,n,r]),g=react.useCallback(async(i=r)=>{if(!n)throw new Error("[MSAL] No active account. Please login first.");try{let a={scopes:i,account:n};await e.acquireTokenRedirect(a);}catch(a){throw console.error("[MSAL] Token redirect acquisition failed:",a),a}},[e,n,r]),y=react.useCallback(async(i=r)=>{let a=`${n?.homeAccountId||"anonymous"}-${i.sort().join(",")}`,b=_.get(a);if(b)return b;let v=(async()=>{try{return await p(i)}catch{return console.warn("[MSAL] Silent token acquisition failed, falling back to popup"),await m(i)}finally{_.delete(a);}})();return _.set(a,v),v},[p,m,r,n]),f=react.useCallback(async()=>{e.setActiveAccount(null),await e.clearCache();},[e]);return {account:n,accounts:t,isAuthenticated:d,inProgress:o!==msalBrowser.InteractionStatus.None,loginPopup:l,loginRedirect:c,logoutPopup:s,logoutRedirect:h,acquireToken:y,acquireTokenSilent:p,acquireTokenPopup:m,acquireTokenRedirect:g,clearSession:f}}function Le({text:r="Sign in with Microsoft",variant:e="dark",size:t="medium",useRedirect:o=false,scopes:n,className:u="",style:d,onSuccess:l,onError:c}){let{loginPopup:s,loginRedirect:h,inProgress:p}=A(),m=async()=>{try{o?await h(n):await s(n),l?.();}catch(i){c?.(i);}},g={small:{padding:"8px 16px",fontSize:"14px",height:"36px"},medium:{padding:"10px 20px",fontSize:"15px",height:"41px"},large:{padding:"12px 24px",fontSize:"16px",height:"48px"}},f={display:"inline-flex",alignItems:"center",justifyContent:"center",gap:"12px",fontFamily:'"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',fontWeight:600,borderRadius:"2px",cursor:p?"not-allowed":"pointer",transition:"all 0.2s ease",opacity:p?.6:1,...{dark:{backgroundColor:"#2F2F2F",color:"#FFFFFF",border:"1px solid #8C8C8C"},light:{backgroundColor:"#FFFFFF",color:"#5E5E5E",border:"1px solid #8C8C8C"}}[e],...g[t],...d};return jsxRuntime.jsxs("button",{onClick:m,disabled:p,className:u,style:f,"aria-label":r,children:[jsxRuntime.jsx(Me,{}),jsxRuntime.jsx("span",{children:r})]})}function Me(){return jsxRuntime.jsxs("svg",{width:"21",height:"21",viewBox:"0 0 21 21",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[jsxRuntime.jsx("rect",{width:"10",height:"10",fill:"#F25022"}),jsxRuntime.jsx("rect",{x:"11",width:"10",height:"10",fill:"#7FBA00"}),jsxRuntime.jsx("rect",{y:"11",width:"10",height:"10",fill:"#00A4EF"}),jsxRuntime.jsx("rect",{x:"11",y:"11",width:"10",height:"10",fill:"#FFB900"})]})}function Ue({text:r="Sign out",variant:e="dark",size:t="medium",useRedirect:o=false,className:n="",style:u,onSuccess:d,onError:l}){let{logoutPopup:c,logoutRedirect:s,inProgress:h}=A(),p=async()=>{try{o?await s():await c(),d?.();}catch(f){l?.(f);}},m={small:{padding:"8px 16px",fontSize:"14px",height:"36px"},medium:{padding:"10px 20px",fontSize:"15px",height:"41px"},large:{padding:"12px 24px",fontSize:"16px",height:"48px"}},y={display:"inline-flex",alignItems:"center",justifyContent:"center",gap:"12px",fontFamily:'"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',fontWeight:600,borderRadius:"2px",cursor:h?"not-allowed":"pointer",transition:"all 0.2s ease",opacity:h?.6:1,...{dark:{backgroundColor:"#2F2F2F",color:"#FFFFFF",border:"1px solid #8C8C8C"},light:{backgroundColor:"#FFFFFF",color:"#5E5E5E",border:"1px solid #8C8C8C"}}[e],...m[t],...u};return jsxRuntime.jsxs("button",{onClick:p,disabled:h,className:n,style:y,"aria-label":r,children:[jsxRuntime.jsx(ke,{}),jsxRuntime.jsx("span",{children:r})]})}function ke(){return jsxRuntime.jsxs("svg",{width:"21",height:"21",viewBox:"0 0 21 21",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[jsxRuntime.jsx("rect",{width:"10",height:"10",fill:"#F25022"}),jsxRuntime.jsx("rect",{x:"11",width:"10",height:"10",fill:"#7FBA00"}),jsxRuntime.jsx("rect",{y:"11",width:"10",height:"10",fill:"#00A4EF"}),jsxRuntime.jsx("rect",{x:"11",y:"11",width:"10",height:"10",fill:"#FFB900"})]})}function U(){let{acquireToken:r}=A(),e=react.useCallback(async(l,c={})=>{let{scopes:s=["User.Read"],version:h="v1.0",debug:p=false,...m}=c;try{let g=await r(s),y=`https://graph.microsoft.com/${h}`,f=l.startsWith("http")?l:`${y}${l.startsWith("/")?l:`/${l}`}`;p&&console.log("[GraphAPI] Request:",{url:f,method:m.method||"GET"});let i=await fetch(f,{...m,headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json",...m.headers}});if(!i.ok){let b=await i.text(),v=`Graph API error (${i.status}): ${b}`;throw new Error(v)}if(i.status===204||i.headers.get("content-length")==="0")return null;let a=await i.json();return p&&console.log("[GraphAPI] Response:",a),a}catch(g){let y=C(g);throw console.error("[GraphAPI] Request failed:",y),new Error(y)}},[r]),t=react.useCallback((l,c={})=>e(l,{...c,method:"GET"}),[e]),o=react.useCallback((l,c,s={})=>e(l,{...s,method:"POST",body:c?JSON.stringify(c):void 0}),[e]),n=react.useCallback((l,c,s={})=>e(l,{...s,method:"PUT",body:c?JSON.stringify(c):void 0}),[e]),u=react.useCallback((l,c,s={})=>e(l,{...s,method:"PATCH",body:c?JSON.stringify(c):void 0}),[e]),d=react.useCallback((l,c={})=>e(l,{...c,method:"DELETE"}),[e]);return {get:t,post:o,put:n,patch:u,delete:d,request:e}}var R=new Map,Fe=300*1e3,ue=100;function Ie(){if(R.size>ue){let r=Array.from(R.entries());r.sort((t,o)=>t[1].timestamp-o[1].timestamp),r.slice(0,R.size-ue).forEach(([t])=>{let o=R.get(t);o?.data.photo&&URL.revokeObjectURL(o.data.photo),R.delete(t);});}}function J(){let{isAuthenticated:r,account:e}=A(),t=U(),[o,n]=react.useState(null),[u,d]=react.useState(false),[l,c]=react.useState(null),s=react.useCallback(async()=>{if(!r||!e){n(null);return}let p=e.homeAccountId,m=R.get(p);if(m&&Date.now()-m.timestamp<Fe){n(m.data);return}d(true),c(null);try{let g=await t.get("/me",{scopes:["User.Read"]}),y;try{let i=await t.get("/me/photo/$value",{scopes:["User.Read"],headers:{"Content-Type":"image/jpeg"}});i&&(y=URL.createObjectURL(i));}catch{console.debug("[UserProfile] Photo not available");}let f={id:g.id,displayName:g.displayName,givenName:g.givenName,surname:g.surname,userPrincipalName:g.userPrincipalName,mail:g.mail,jobTitle:g.jobTitle,officeLocation:g.officeLocation,mobilePhone:g.mobilePhone,businessPhones:g.businessPhones,photo:y};R.set(p,{data:f,timestamp:Date.now()}),Ie(),n(f);}catch(g){let f=C(g),i=new Error(f);c(i),console.error("[UserProfile] Failed to fetch profile:",f);}finally{d(false);}},[r,e,t]),h=react.useCallback(()=>{if(e){let p=R.get(e.homeAccountId);p?.data.photo&&URL.revokeObjectURL(p.data.photo),R.delete(e.homeAccountId);}o?.photo&&URL.revokeObjectURL(o.photo),n(null);},[e,o]);return react.useEffect(()=>(s(),()=>{o?.photo&&URL.revokeObjectURL(o.photo);}),[s]),react.useEffect(()=>()=>{o?.photo&&URL.revokeObjectURL(o.photo);},[o?.photo]),{profile:o,loading:u,error:l,refetch:s,clearCache:h}}function De({size:r=40,className:e="",style:t,showTooltip:o=true,fallbackImage:n}){let{profile:u,loading:d}=J(),[l,c]=react.useState(null),[s,h]=react.useState(false);react.useEffect(()=>{u?.photo&&c(u.photo);},[u?.photo]);let p=()=>{if(!u)return "?";let{givenName:y,surname:f,displayName:i}=u;if(y&&f)return `${y[0]}${f[0]}`.toUpperCase();if(i){let a=i.split(" ");return a.length>=2?`${a[0][0]}${a[a.length-1][0]}`.toUpperCase():i.substring(0,2).toUpperCase()}return "?"},m={width:`${r}px`,height:`${r}px`,borderRadius:"50%",display:"inline-flex",alignItems:"center",justifyContent:"center",fontSize:`${r*.4}px`,fontWeight:600,fontFamily:'"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',backgroundColor:"#0078D4",color:"#FFFFFF",overflow:"hidden",userSelect:"none",...t},g=u?.displayName||"User";return d?jsxRuntime.jsx("div",{className:e,style:{...m,backgroundColor:"#E1E1E1"},"aria-label":"Loading user avatar",children:jsxRuntime.jsx("span",{style:{fontSize:`${r*.3}px`},children:"..."})}):l&&!s?jsxRuntime.jsx("div",{className:e,style:m,title:o?g:void 0,"aria-label":`${g} avatar`,children:jsxRuntime.jsx("img",{src:l,alt:g,style:{width:"100%",height:"100%",objectFit:"cover"},onError:()=>{h(true),n&&c(n);}})}):jsxRuntime.jsx("div",{className:e,style:m,title:o?g:void 0,"aria-label":`${g} avatar`,children:p()})}function Oe({className:r="",style:e,showDetails:t=false,renderLoading:o,renderAuthenticated:n,renderUnauthenticated:u}){let{isAuthenticated:d,inProgress:l,account:c}=A(),s={display:"inline-flex",alignItems:"center",gap:"8px",padding:"8px 12px",borderRadius:"4px",fontFamily:'"Segoe UI", Tahoma, Geneva, Verdana, sans-serif',fontSize:"14px",fontWeight:500,...e};if(l)return o?jsxRuntime.jsx(jsxRuntime.Fragment,{children:o()}):jsxRuntime.jsxs("div",{className:r,style:{...s,backgroundColor:"#FFF4CE",color:"#8A6D3B"},role:"status","aria-live":"polite",children:[jsxRuntime.jsx(Z,{color:"#FFA500"}),jsxRuntime.jsx("span",{children:"Loading..."})]});if(d){let h=c?.username||c?.name||"User";return n?jsxRuntime.jsx(jsxRuntime.Fragment,{children:n(h)}):jsxRuntime.jsxs("div",{className:r,style:{...s,backgroundColor:"#D4EDDA",color:"#155724"},role:"status","aria-live":"polite",children:[jsxRuntime.jsx(Z,{color:"#28A745"}),jsxRuntime.jsx("span",{children:t?`Authenticated as ${h}`:"Authenticated"})]})}return u?jsxRuntime.jsx(jsxRuntime.Fragment,{children:u()}):jsxRuntime.jsxs("div",{className:r,style:{...s,backgroundColor:"#F8D7DA",color:"#721C24"},role:"status","aria-live":"polite",children:[jsxRuntime.jsx(Z,{color:"#DC3545"}),jsxRuntime.jsx("span",{children:"Not authenticated"})]})}function Z({color:r}){return jsxRuntime.jsx("svg",{width:"8",height:"8",viewBox:"0 0 8 8",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:jsxRuntime.jsx("circle",{cx:"4",cy:"4",r:"4",fill:r})})}function Q({children:r,loadingComponent:e,fallbackComponent:t,useRedirect:o=true,scopes:n,onAuthRequired:u}){let{isAuthenticated:d,inProgress:l,loginRedirect:c,loginPopup:s}=A();return react.useEffect(()=>{!d&&!l&&(u?.(),(async()=>{try{o?await c(n):await s(n);}catch(p){console.error("[AuthGuard] Authentication failed:",p);}})());},[d,l,o,n,c,s,u]),l?jsxRuntime.jsx(jsxRuntime.Fragment,{children:e||jsxRuntime.jsx("div",{children:"Authenticating..."})}):d?jsxRuntime.jsx(jsxRuntime.Fragment,{children:r}):jsxRuntime.jsx(jsxRuntime.Fragment,{children:t||jsxRuntime.jsx("div",{children:"Redirecting to login..."})})}var ee=class extends react.Component{constructor(t){super(t);this.reset=()=>{this.setState({hasError:false,error:null});};this.state={hasError:false,error:null};}static getDerivedStateFromError(t){return {hasError:true,error:t}}componentDidCatch(t,o){let{onError:n,debug:u}=this.props;u&&(console.error("[ErrorBoundary] Caught error:",t),console.error("[ErrorBoundary] Error info:",o)),n?.(t,o);}render(){let{hasError:t,error:o}=this.state,{children:n,fallback:u}=this.props;return t&&o?u?u(o,this.reset):jsxRuntime.jsxs("div",{style:{padding:"20px",margin:"20px",border:"1px solid #DC3545",borderRadius:"4px",backgroundColor:"#F8D7DA",color:"#721C24",fontFamily:'"Segoe UI", Tahoma, Geneva, Verdana, sans-serif'},children:[jsxRuntime.jsx("h2",{style:{margin:"0 0 10px 0",fontSize:"18px"},children:"Authentication Error"}),jsxRuntime.jsx("p",{style:{margin:"0 0 10px 0"},children:o.message}),jsxRuntime.jsx("button",{onClick:this.reset,style:{padding:"8px 16px",backgroundColor:"#DC3545",color:"#FFFFFF",border:"none",borderRadius:"4px",cursor:"pointer",fontSize:"14px",fontWeight:600},children:"Try Again"})]}):n}};var w=new Map,Be=300*1e3,de=100;function Ve(r){r?w.delete(r):w.clear();}function We(){if(w.size>de){let r=Array.from(w.entries());r.sort((t,o)=>t[1].timestamp-o[1].timestamp),r.slice(0,w.size-de).forEach(([t])=>w.delete(t));}}function _e(){let{isAuthenticated:r,account:e}=A(),t=U(),[o,n]=react.useState([]),[u,d]=react.useState([]),[l,c]=react.useState(false),[s,h]=react.useState(null),p=react.useCallback(async()=>{if(!r||!e){n([]),d([]);return}let i=e.homeAccountId,a=w.get(i);if(a&&Date.now()-a.timestamp<Be){n(a.roles),d(a.groups);return}c(true),h(null);try{let v=e.idTokenClaims?.roles||[],O=(await t.get("/me/memberOf",{scopes:["User.Read","Directory.Read.All"]})).value.map(te=>te.id);w.set(i,{roles:v,groups:O,timestamp:Date.now()}),We(),n(v),d(O);}catch(b){let D=C(b),O=new Error(D);h(O),console.error("[Roles] Failed to fetch roles/groups:",D);let he=e.idTokenClaims?.roles||[];n(he);}finally{c(false);}},[r,e,t]),m=react.useCallback(i=>o.includes(i),[o]),g=react.useCallback(i=>u.includes(i),[u]),y=react.useCallback(i=>i.some(a=>o.includes(a)),[o]),f=react.useCallback(i=>i.every(a=>o.includes(a)),[o]);return react.useEffect(()=>(p(),()=>{e&&Ve(e.homeAccountId);}),[p,e]),{roles:o,groups:u,loading:l,error:s,hasRole:m,hasGroup:g,hasAnyRole:y,hasAllRoles:f,refetch:p}}function je(r,e={}){let{displayName:t,...o}=e,n=u=>jsxRuntime.jsx(Q,{...o,children:jsxRuntime.jsx(r,{...u})});return n.displayName=t||`withAuth(${r.displayName||r.name||"Component"})`,n}async function fe(r,e={}){let{maxRetries:t=3,initialDelay:o=1e3,maxDelay:n=1e4,backoffMultiplier:u=2,debug:d=false}=e,l,c=o;for(let s=0;s<=t;s++)try{return d&&s>0&&console.log(`[TokenRetry] Attempt ${s+1}/${t+1}`),await r()}catch(h){if(l=h,s===t){d&&console.error("[TokenRetry] All retry attempts failed");break}if(!Je(h))throw d&&console.log("[TokenRetry] Non-retryable error, aborting"),h;d&&console.warn(`[TokenRetry] Attempt ${s+1} failed, retrying in ${c}ms...`),await Ze(c),c=Math.min(c*u,n);}throw l}function Je(r){let e=r.message.toLowerCase();return !!(e.includes("network")||e.includes("timeout")||e.includes("fetch")||e.includes("connection")||e.includes("500")||e.includes("502")||e.includes("503")||e.includes("429")||e.includes("rate limit")||e.includes("token")&&e.includes("expired"))}function Ze(r){return new Promise(e=>setTimeout(e,r))}function He(r,e={}){return (...t)=>fe(()=>r(...t),e)}var $=class{constructor(e={}){this.config={enabled:e.enabled??false,prefix:e.prefix??"[MSAL-Next]",showTimestamp:e.showTimestamp??true,level:e.level??"info"};}shouldLog(e){if(!this.config.enabled)return false;let t=["error","warn","info","debug"],o=t.indexOf(this.config.level);return t.indexOf(e)<=o}formatMessage(e,t,o){let n=this.config.showTimestamp?`[${new Date().toISOString()}]`:"",u=this.config.prefix,d=`[${e.toUpperCase()}]`,l=`${n} ${u} ${d} ${t}`;return o!==void 0&&(l+=`
|
|
2
|
+
`+JSON.stringify(o,null,2)),l}error(e,t){this.shouldLog("error")&&console.error(this.formatMessage("error",e,t));}warn(e,t){this.shouldLog("warn")&&console.warn(this.formatMessage("warn",e,t));}info(e,t){this.shouldLog("info")&&console.info(this.formatMessage("info",e,t));}debug(e,t){this.shouldLog("debug")&&console.debug(this.formatMessage("debug",e,t));}group(e){this.config.enabled&&console.group(`${this.config.prefix} ${e}`);}groupEnd(){this.config.enabled&&console.groupEnd();}setEnabled(e){this.config.enabled=e;}setLevel(e){e&&(this.config.level=e);}},N=null;function Ke(r){return N?r&&(r.enabled!==void 0&&N.setEnabled(r.enabled),r.level&&N.setLevel(r.level)):N=new $(r),N}function Xe(r,e){return new $({...e,prefix:`[MSAL-Next:${r}]`})}function Qe(r={}){let{protectedRoutes:e=[],publicOnlyRoutes:t=[],loginPath:o="/login",redirectAfterLogin:n="/",sessionCookie:u="msal.account",isAuthenticated:d,debug:l=false}=r;return async function(s){let{pathname:h}=s.nextUrl;l&&console.log("[AuthMiddleware] Processing:",h);let p=false;d?p=await d(s):p=!!s.cookies.get(u)?.value,l&&console.log("[AuthMiddleware] Authenticated:",p);let m=e.some(f=>h.startsWith(f)),g=t.some(f=>h.startsWith(f));if(m&&!p){l&&console.log("[AuthMiddleware] Redirecting to login");let f=s.nextUrl.clone();return f.pathname=o,f.searchParams.set("returnUrl",h),server.NextResponse.redirect(f)}if(g&&p){l&&console.log("[AuthMiddleware] Redirecting to home");let f=s.nextUrl.searchParams.get("returnUrl"),i=s.nextUrl.clone();return i.pathname=f||n,i.searchParams.delete("returnUrl"),server.NextResponse.redirect(i)}let y=server.NextResponse.next();if(p){y.headers.set("x-msal-authenticated","true");try{let f=s.cookies.get(u);if(f?.value){let i=z(f.value,B);i?.username&&y.headers.set("x-msal-username",i.username);}}catch{l&&console.warn("[AuthMiddleware] Failed to parse session data");}}return y}}Object.defineProperty(exports,"useAccount",{enumerable:true,get:function(){return msalReact.useAccount}});Object.defineProperty(exports,"useIsAuthenticated",{enumerable:true,get:function(){return msalReact.useIsAuthenticated}});Object.defineProperty(exports,"useMsal",{enumerable:true,get:function(){return msalReact.useMsal}});exports.AuthGuard=Q;exports.AuthStatus=Oe;exports.ErrorBoundary=ee;exports.MicrosoftSignInButton=Le;exports.MsalAuthProvider=ve;exports.SignOutButton=Ue;exports.UserAvatar=De;exports.createAuthMiddleware=Qe;exports.createMsalConfig=V;exports.createRetryWrapper=He;exports.createScopedLogger=Xe;exports.getDebugLogger=Ke;exports.getMsalInstance=Re;exports.isValidAccountData=B;exports.isValidRedirectUri=G;exports.isValidScope=oe;exports.retryWithBackoff=fe;exports.safeJsonParse=z;exports.sanitizeError=C;exports.useGraphApi=U;exports.useMsalAuth=A;exports.useRoles=_e;exports.useUserProfile=J;exports.validateScopes=me;exports.withAuth=je;
|