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