@chemmangat/msal-next 1.2.1 → 2.0.1
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/README.md +551 -290
- package/SECURITY.md +152 -0
- package/dist/index.d.mts +632 -2
- package/dist/index.d.ts +632 -2
- package/dist/index.js +1092 -102
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1026 -41
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +54 -0
- package/dist/server.d.ts +54 -0
- package/dist/server.js +91 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +88 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +77 -61
package/dist/index.js
CHANGED
|
@@ -1,43 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
"use strict";
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
'use strict';
|
|
20
2
|
|
|
21
|
-
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
getMsalInstance: () => getMsalInstance,
|
|
27
|
-
useAccount: () => import_msal_react3.useAccount,
|
|
28
|
-
useIsAuthenticated: () => import_msal_react3.useIsAuthenticated,
|
|
29
|
-
useMsal: () => import_msal_react3.useMsal,
|
|
30
|
-
useMsalAuth: () => useMsalAuth
|
|
31
|
-
});
|
|
32
|
-
module.exports = __toCommonJS(index_exports);
|
|
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');
|
|
33
8
|
|
|
34
|
-
// src/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
+
}
|
|
38
51
|
|
|
39
52
|
// src/utils/createMsalConfig.ts
|
|
40
|
-
var import_msal_browser = require("@azure/msal-browser");
|
|
41
53
|
function createMsalConfig(config) {
|
|
42
54
|
if (config.msalConfig) {
|
|
43
55
|
return config.msalConfig;
|
|
@@ -52,7 +64,8 @@ function createMsalConfig(config) {
|
|
|
52
64
|
storeAuthStateInCookie = false,
|
|
53
65
|
navigateToLoginRequestUrl = true,
|
|
54
66
|
enableLogging = false,
|
|
55
|
-
loggerCallback
|
|
67
|
+
loggerCallback,
|
|
68
|
+
allowedRedirectUris
|
|
56
69
|
} = config;
|
|
57
70
|
if (!clientId) {
|
|
58
71
|
throw new Error("@chemmangat/msal-next: clientId is required");
|
|
@@ -68,6 +81,19 @@ function createMsalConfig(config) {
|
|
|
68
81
|
};
|
|
69
82
|
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
70
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
|
+
}
|
|
71
97
|
const msalConfig = {
|
|
72
98
|
auth: {
|
|
73
99
|
clientId,
|
|
@@ -85,37 +111,34 @@ function createMsalConfig(config) {
|
|
|
85
111
|
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
86
112
|
if (containsPii || !enableLogging) return;
|
|
87
113
|
switch (level) {
|
|
88
|
-
case
|
|
114
|
+
case msalBrowser.LogLevel.Error:
|
|
89
115
|
console.error("[MSAL]", message);
|
|
90
116
|
break;
|
|
91
|
-
case
|
|
117
|
+
case msalBrowser.LogLevel.Warning:
|
|
92
118
|
console.warn("[MSAL]", message);
|
|
93
119
|
break;
|
|
94
|
-
case
|
|
120
|
+
case msalBrowser.LogLevel.Info:
|
|
95
121
|
console.info("[MSAL]", message);
|
|
96
122
|
break;
|
|
97
|
-
case
|
|
123
|
+
case msalBrowser.LogLevel.Verbose:
|
|
98
124
|
console.debug("[MSAL]", message);
|
|
99
125
|
break;
|
|
100
126
|
}
|
|
101
127
|
}),
|
|
102
|
-
logLevel: enableLogging ?
|
|
128
|
+
logLevel: enableLogging ? msalBrowser.LogLevel.Verbose : msalBrowser.LogLevel.Error
|
|
103
129
|
}
|
|
104
130
|
}
|
|
105
131
|
};
|
|
106
132
|
return msalConfig;
|
|
107
133
|
}
|
|
108
|
-
|
|
109
|
-
// src/components/MsalAuthProvider.tsx
|
|
110
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
111
134
|
var globalMsalInstance = null;
|
|
112
135
|
function getMsalInstance() {
|
|
113
136
|
return globalMsalInstance;
|
|
114
137
|
}
|
|
115
138
|
function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config }) {
|
|
116
|
-
const [msalInstance, setMsalInstance] =
|
|
117
|
-
const instanceRef =
|
|
118
|
-
|
|
139
|
+
const [msalInstance, setMsalInstance] = react.useState(null);
|
|
140
|
+
const instanceRef = react.useRef(null);
|
|
141
|
+
react.useEffect(() => {
|
|
119
142
|
if (typeof window === "undefined") {
|
|
120
143
|
return;
|
|
121
144
|
}
|
|
@@ -125,7 +148,7 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
125
148
|
const initializeMsal = async () => {
|
|
126
149
|
try {
|
|
127
150
|
const msalConfig = createMsalConfig(config);
|
|
128
|
-
const instance = new
|
|
151
|
+
const instance = new msalBrowser.PublicClientApplication(msalConfig);
|
|
129
152
|
await instance.initialize();
|
|
130
153
|
const response = await instance.handleRedirectPromise();
|
|
131
154
|
if (response && config.enableLogging) {
|
|
@@ -133,16 +156,16 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
133
156
|
}
|
|
134
157
|
const enableLogging = config.enableLogging || false;
|
|
135
158
|
instance.addEventCallback((event) => {
|
|
136
|
-
if (event.eventType ===
|
|
159
|
+
if (event.eventType === msalBrowser.EventType.LOGIN_SUCCESS) {
|
|
137
160
|
if (enableLogging) {
|
|
138
161
|
const payload = event.payload;
|
|
139
162
|
console.log("[MSAL] Login successful:", payload.account?.username);
|
|
140
163
|
}
|
|
141
164
|
}
|
|
142
|
-
if (event.eventType ===
|
|
165
|
+
if (event.eventType === msalBrowser.EventType.LOGIN_FAILURE) {
|
|
143
166
|
console.error("[MSAL] Login failed:", event.error);
|
|
144
167
|
}
|
|
145
|
-
if (event.eventType ===
|
|
168
|
+
if (event.eventType === msalBrowser.EventType.LOGOUT_SUCCESS) {
|
|
146
169
|
if (enableLogging) {
|
|
147
170
|
console.log("[MSAL] Logout successful");
|
|
148
171
|
}
|
|
@@ -162,23 +185,20 @@ function MsalAuthProvider({ children, loadingComponent, onInitialized, ...config
|
|
|
162
185
|
initializeMsal();
|
|
163
186
|
}, []);
|
|
164
187
|
if (typeof window === "undefined") {
|
|
165
|
-
return /* @__PURE__ */
|
|
188
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading authentication..." }) });
|
|
166
189
|
}
|
|
167
190
|
if (!msalInstance) {
|
|
168
|
-
return /* @__PURE__ */
|
|
191
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading authentication..." }) });
|
|
169
192
|
}
|
|
170
|
-
return /* @__PURE__ */
|
|
193
|
+
return /* @__PURE__ */ jsxRuntime.jsx(msalReact.MsalProvider, { instance: msalInstance, children });
|
|
171
194
|
}
|
|
172
|
-
|
|
173
|
-
// src/hooks/useMsalAuth.ts
|
|
174
|
-
var import_msal_react2 = require("@azure/msal-react");
|
|
175
|
-
var import_msal_browser3 = require("@azure/msal-browser");
|
|
176
|
-
var import_react2 = require("react");
|
|
195
|
+
var pendingTokenRequests = /* @__PURE__ */ new Map();
|
|
177
196
|
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
178
|
-
const { instance, accounts, inProgress } =
|
|
179
|
-
const account =
|
|
180
|
-
const
|
|
181
|
-
const
|
|
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(
|
|
182
202
|
async (scopes = defaultScopes) => {
|
|
183
203
|
try {
|
|
184
204
|
const request = {
|
|
@@ -193,7 +213,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
193
213
|
},
|
|
194
214
|
[instance, defaultScopes]
|
|
195
215
|
);
|
|
196
|
-
const loginRedirect =
|
|
216
|
+
const loginRedirect = react.useCallback(
|
|
197
217
|
async (scopes = defaultScopes) => {
|
|
198
218
|
try {
|
|
199
219
|
const request = {
|
|
@@ -208,7 +228,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
208
228
|
},
|
|
209
229
|
[instance, defaultScopes]
|
|
210
230
|
);
|
|
211
|
-
const logoutPopup =
|
|
231
|
+
const logoutPopup = react.useCallback(async () => {
|
|
212
232
|
try {
|
|
213
233
|
await instance.logoutPopup({
|
|
214
234
|
account: account || void 0
|
|
@@ -218,7 +238,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
218
238
|
throw error;
|
|
219
239
|
}
|
|
220
240
|
}, [instance, account]);
|
|
221
|
-
const logoutRedirect =
|
|
241
|
+
const logoutRedirect = react.useCallback(async () => {
|
|
222
242
|
try {
|
|
223
243
|
await instance.logoutRedirect({
|
|
224
244
|
account: account || void 0
|
|
@@ -228,7 +248,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
228
248
|
throw error;
|
|
229
249
|
}
|
|
230
250
|
}, [instance, account]);
|
|
231
|
-
const acquireTokenSilent =
|
|
251
|
+
const acquireTokenSilent = react.useCallback(
|
|
232
252
|
async (scopes = defaultScopes) => {
|
|
233
253
|
if (!account) {
|
|
234
254
|
throw new Error("[MSAL] No active account. Please login first.");
|
|
@@ -236,7 +256,8 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
236
256
|
try {
|
|
237
257
|
const request = {
|
|
238
258
|
scopes,
|
|
239
|
-
account
|
|
259
|
+
account,
|
|
260
|
+
forceRefresh: false
|
|
240
261
|
};
|
|
241
262
|
const response = await instance.acquireTokenSilent(request);
|
|
242
263
|
return response.accessToken;
|
|
@@ -247,12 +268,16 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
247
268
|
},
|
|
248
269
|
[instance, account, defaultScopes]
|
|
249
270
|
);
|
|
250
|
-
const acquireTokenPopup =
|
|
271
|
+
const acquireTokenPopup = react.useCallback(
|
|
251
272
|
async (scopes = defaultScopes) => {
|
|
252
273
|
if (!account) {
|
|
253
274
|
throw new Error("[MSAL] No active account. Please login first.");
|
|
254
275
|
}
|
|
276
|
+
if (popupInProgressRef.current) {
|
|
277
|
+
throw new Error("[MSAL] Popup already in progress. Please wait.");
|
|
278
|
+
}
|
|
255
279
|
try {
|
|
280
|
+
popupInProgressRef.current = true;
|
|
256
281
|
const request = {
|
|
257
282
|
scopes,
|
|
258
283
|
account
|
|
@@ -262,11 +287,13 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
262
287
|
} catch (error) {
|
|
263
288
|
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
264
289
|
throw error;
|
|
290
|
+
} finally {
|
|
291
|
+
popupInProgressRef.current = false;
|
|
265
292
|
}
|
|
266
293
|
},
|
|
267
294
|
[instance, account, defaultScopes]
|
|
268
295
|
);
|
|
269
|
-
const acquireTokenRedirect =
|
|
296
|
+
const acquireTokenRedirect = react.useCallback(
|
|
270
297
|
async (scopes = defaultScopes) => {
|
|
271
298
|
if (!account) {
|
|
272
299
|
throw new Error("[MSAL] No active account. Please login first.");
|
|
@@ -284,18 +311,29 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
284
311
|
},
|
|
285
312
|
[instance, account, defaultScopes]
|
|
286
313
|
);
|
|
287
|
-
const acquireToken =
|
|
314
|
+
const acquireToken = react.useCallback(
|
|
288
315
|
async (scopes = defaultScopes) => {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return await acquireTokenPopup(scopes);
|
|
316
|
+
const requestKey = `${account?.homeAccountId || "anonymous"}-${scopes.sort().join(",")}`;
|
|
317
|
+
const pendingRequest = pendingTokenRequests.get(requestKey);
|
|
318
|
+
if (pendingRequest) {
|
|
319
|
+
return pendingRequest;
|
|
294
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;
|
|
295
333
|
},
|
|
296
|
-
[acquireTokenSilent, acquireTokenPopup, defaultScopes]
|
|
334
|
+
[acquireTokenSilent, acquireTokenPopup, defaultScopes, account]
|
|
297
335
|
);
|
|
298
|
-
const clearSession =
|
|
336
|
+
const clearSession = react.useCallback(async () => {
|
|
299
337
|
instance.setActiveAccount(null);
|
|
300
338
|
await instance.clearCache();
|
|
301
339
|
}, [instance]);
|
|
@@ -303,7 +341,7 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
303
341
|
account,
|
|
304
342
|
accounts,
|
|
305
343
|
isAuthenticated,
|
|
306
|
-
inProgress: inProgress !==
|
|
344
|
+
inProgress: inProgress !== msalBrowser.InteractionStatus.None,
|
|
307
345
|
loginPopup,
|
|
308
346
|
loginRedirect,
|
|
309
347
|
logoutPopup,
|
|
@@ -315,9 +353,6 @@ function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
|
315
353
|
clearSession
|
|
316
354
|
};
|
|
317
355
|
}
|
|
318
|
-
|
|
319
|
-
// src/components/MicrosoftSignInButton.tsx
|
|
320
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
321
356
|
function MicrosoftSignInButton({
|
|
322
357
|
text = "Sign in with Microsoft",
|
|
323
358
|
variant = "dark",
|
|
@@ -386,7 +421,7 @@ function MicrosoftSignInButton({
|
|
|
386
421
|
...sizeStyles[size],
|
|
387
422
|
...style
|
|
388
423
|
};
|
|
389
|
-
return /* @__PURE__ */
|
|
424
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
390
425
|
"button",
|
|
391
426
|
{
|
|
392
427
|
onClick: handleClick,
|
|
@@ -395,31 +430,986 @@ function MicrosoftSignInButton({
|
|
|
395
430
|
style: baseStyles,
|
|
396
431
|
"aria-label": text,
|
|
397
432
|
children: [
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
433
|
+
/* @__PURE__ */ jsxRuntime.jsx(MicrosoftLogo, {}),
|
|
434
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: text })
|
|
400
435
|
]
|
|
401
436
|
}
|
|
402
437
|
);
|
|
403
438
|
}
|
|
404
439
|
function MicrosoftLogo() {
|
|
405
|
-
return /* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
409
|
-
/* @__PURE__ */
|
|
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" })
|
|
410
445
|
] });
|
|
411
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
|
+
}
|
|
412
634
|
|
|
413
|
-
// src/
|
|
414
|
-
var
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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; }
|
|
424
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
|
|
425
1415
|
//# sourceMappingURL=index.js.map
|