@chemmangat/msal-next 5.2.1 → 5.3.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/CHANGELOG.md +36 -1
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +55 -19
- package/dist/index.mjs +51 -15
- package/dist/server.d.mts +24 -5
- package/dist/server.d.ts +24 -5
- package/dist/server.js +16 -14
- package/dist/server.mjs +16 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [5.
|
|
5
|
+
## [5.3.0] - 2026-04-07
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug Fixes
|
|
8
|
+
|
|
9
|
+
#### Fix 1 — Auto-sync `msal.account` session cookie (no manual cookie code needed)
|
|
10
|
+
|
|
11
|
+
`MsalAuthProvider` now automatically writes and clears the `msal.account` cookie on every auth event. The middleware works out of the box with zero manual setup.
|
|
12
|
+
|
|
13
|
+
- `LOGIN_SUCCESS` → writes `msal.account` cookie (`{ homeAccountId, username, name }`, URL-encoded, `path=/; SameSite=Lax`)
|
|
14
|
+
- `LOGOUT_SUCCESS` / `LOGOUT_END` → clears the cookie
|
|
15
|
+
- Redirect flow (`handleRedirectPromise`) → writes cookie immediately after redirect completes
|
|
16
|
+
- Existing cached account on init → restores cookie (handles browser restart with `localStorage` cache)
|
|
17
|
+
|
|
18
|
+
#### Fix 2 — `setServerSessionCookie` no longer calls a non-existent API route
|
|
19
|
+
|
|
20
|
+
The old implementation called `fetch('/api/auth/session')` which required an undocumented route that didn't exist. It now writes `document.cookie` directly — same format as Fix 1. The function signature changed from `async (account, accessToken?) => Promise<void>` to `(account) => void`.
|
|
21
|
+
|
|
22
|
+
A new `clearServerSessionCookie()` helper is also exported from `@chemmangat/msal-next/server`.
|
|
23
|
+
|
|
24
|
+
> As of v5.3.0 you rarely need to call either function manually — `MsalAuthProvider` handles the cookie automatically.
|
|
25
|
+
|
|
26
|
+
#### Fix 3 — `useTokenRefresh` now reads real token expiry
|
|
27
|
+
|
|
28
|
+
The hook previously hardcoded `expiresIn = 3600`. It now calls `instance.acquireTokenSilent` directly and reads `response.expiresOn` to calculate the actual remaining seconds. Falls back to `3600` if `expiresOn` is `null`.
|
|
29
|
+
|
|
30
|
+
#### Fix 4 — `navigateToLoginRequestUrl` JSDoc default corrected
|
|
31
|
+
|
|
32
|
+
The `@defaultValue` in `MsalAuthConfig` was documented as `true` but the actual default in `createMsalConfig` was `false`. Corrected to `@defaultValue false`.
|
|
33
|
+
|
|
34
|
+
#### Fix 5 — No `/api/auth/session` route required
|
|
35
|
+
|
|
36
|
+
All references to the non-existent `/api/auth/session` route have been removed. Cookie management is now entirely client-side via `document.cookie`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
|
|
6
41
|
|
|
7
42
|
### 🐛 Bug Fix
|
|
8
43
|
|
package/dist/index.d.mts
CHANGED
|
@@ -167,7 +167,7 @@ interface MsalAuthConfig {
|
|
|
167
167
|
* @remarks
|
|
168
168
|
* If true, redirects to the page that initiated login after successful auth.
|
|
169
169
|
*
|
|
170
|
-
* @defaultValue
|
|
170
|
+
* @defaultValue false
|
|
171
171
|
*/
|
|
172
172
|
navigateToLoginRequestUrl?: boolean;
|
|
173
173
|
/**
|
|
@@ -1037,6 +1037,11 @@ interface GraphApiOptions extends RequestInit {
|
|
|
1037
1037
|
* @default false
|
|
1038
1038
|
*/
|
|
1039
1039
|
debug?: boolean;
|
|
1040
|
+
/**
|
|
1041
|
+
* Expected response type. Use 'blob' for binary data like images.
|
|
1042
|
+
* @default 'json'
|
|
1043
|
+
*/
|
|
1044
|
+
responseType?: 'json' | 'blob' | 'text';
|
|
1040
1045
|
}
|
|
1041
1046
|
interface UseGraphApiReturn {
|
|
1042
1047
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -167,7 +167,7 @@ interface MsalAuthConfig {
|
|
|
167
167
|
* @remarks
|
|
168
168
|
* If true, redirects to the page that initiated login after successful auth.
|
|
169
169
|
*
|
|
170
|
-
* @defaultValue
|
|
170
|
+
* @defaultValue false
|
|
171
171
|
*/
|
|
172
172
|
navigateToLoginRequestUrl?: boolean;
|
|
173
173
|
/**
|
|
@@ -1037,6 +1037,11 @@ interface GraphApiOptions extends RequestInit {
|
|
|
1037
1037
|
* @default false
|
|
1038
1038
|
*/
|
|
1039
1039
|
debug?: boolean;
|
|
1040
|
+
/**
|
|
1041
|
+
* Expected response type. Use 'blob' for binary data like images.
|
|
1042
|
+
* @default 'json'
|
|
1043
|
+
*/
|
|
1044
|
+
responseType?: 'json' | 'blob' | 'text';
|
|
1040
1045
|
}
|
|
1041
1046
|
interface UseGraphApiReturn {
|
|
1042
1047
|
/**
|
package/dist/index.js
CHANGED
|
@@ -46,10 +46,10 @@ __export(client_exports, {
|
|
|
46
46
|
retryWithBackoff: () => retryWithBackoff,
|
|
47
47
|
safeJsonParse: () => safeJsonParse,
|
|
48
48
|
sanitizeError: () => sanitizeError,
|
|
49
|
-
useAccount: () =>
|
|
49
|
+
useAccount: () => import_msal_react5.useAccount,
|
|
50
50
|
useGraphApi: () => useGraphApi,
|
|
51
|
-
useIsAuthenticated: () =>
|
|
52
|
-
useMsal: () =>
|
|
51
|
+
useIsAuthenticated: () => import_msal_react5.useIsAuthenticated,
|
|
52
|
+
useMsal: () => import_msal_react5.useMsal,
|
|
53
53
|
useMsalAuth: () => useMsalAuth,
|
|
54
54
|
useMultiAccount: () => useMultiAccount,
|
|
55
55
|
useRoles: () => useRoles,
|
|
@@ -66,7 +66,7 @@ __export(client_exports, {
|
|
|
66
66
|
module.exports = __toCommonJS(client_exports);
|
|
67
67
|
|
|
68
68
|
// src/components/MsalAuthProvider.tsx
|
|
69
|
-
var
|
|
69
|
+
var import_msal_react3 = require("@azure/msal-react");
|
|
70
70
|
var import_msal_browser3 = require("@azure/msal-browser");
|
|
71
71
|
var import_react3 = require("react");
|
|
72
72
|
|
|
@@ -673,6 +673,7 @@ Note: Environment variables starting with NEXT_PUBLIC_ are exposed to the browse
|
|
|
673
673
|
|
|
674
674
|
// src/hooks/useTokenRefresh.ts
|
|
675
675
|
var import_react2 = require("react");
|
|
676
|
+
var import_msal_react2 = require("@azure/msal-react");
|
|
676
677
|
|
|
677
678
|
// src/hooks/useMsalAuth.ts
|
|
678
679
|
var import_msal_react = require("@azure/msal-react");
|
|
@@ -851,7 +852,8 @@ function useTokenRefresh(options = {}) {
|
|
|
851
852
|
onRefresh,
|
|
852
853
|
onError
|
|
853
854
|
} = options;
|
|
854
|
-
const { isAuthenticated, account
|
|
855
|
+
const { isAuthenticated, account } = useMsalAuth();
|
|
856
|
+
const { instance } = (0, import_msal_react2.useMsal)();
|
|
855
857
|
const intervalRef = (0, import_react2.useRef)(null);
|
|
856
858
|
const lastRefreshRef = (0, import_react2.useRef)(null);
|
|
857
859
|
const expiresInRef = (0, import_react2.useRef)(null);
|
|
@@ -860,23 +862,27 @@ function useTokenRefresh(options = {}) {
|
|
|
860
862
|
return;
|
|
861
863
|
}
|
|
862
864
|
try {
|
|
863
|
-
await acquireTokenSilent(
|
|
865
|
+
const response = await instance.acquireTokenSilent({
|
|
866
|
+
scopes,
|
|
867
|
+
account,
|
|
868
|
+
forceRefresh: false
|
|
869
|
+
});
|
|
864
870
|
lastRefreshRef.current = /* @__PURE__ */ new Date();
|
|
865
|
-
const expiresIn = 3600;
|
|
871
|
+
const expiresIn = response.expiresOn ? Math.max(0, response.expiresOn.getTime() / 1e3 - Date.now() / 1e3) : 3600;
|
|
866
872
|
expiresInRef.current = expiresIn;
|
|
867
873
|
onRefresh?.(expiresIn);
|
|
868
874
|
} catch (error) {
|
|
869
875
|
console.error("[TokenRefresh] Failed to refresh token:", error);
|
|
870
876
|
onError?.(error);
|
|
871
877
|
}
|
|
872
|
-
}, [isAuthenticated, account,
|
|
878
|
+
}, [isAuthenticated, account, instance, scopes, onRefresh, onError]);
|
|
873
879
|
(0, import_react2.useEffect)(() => {
|
|
874
880
|
if (!enabled || !isAuthenticated) {
|
|
875
881
|
return;
|
|
876
882
|
}
|
|
877
883
|
refresh();
|
|
878
884
|
intervalRef.current = setInterval(() => {
|
|
879
|
-
if (
|
|
885
|
+
if (expiresInRef.current === null) {
|
|
880
886
|
return;
|
|
881
887
|
}
|
|
882
888
|
const timeSinceRefresh = lastRefreshRef.current ? (Date.now() - lastRefreshRef.current.getTime()) / 1e3 : 0;
|
|
@@ -1009,6 +1015,23 @@ function validateTenantAccess(account, config) {
|
|
|
1009
1015
|
// src/components/MsalAuthProvider.tsx
|
|
1010
1016
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1011
1017
|
var globalMsalInstance = null;
|
|
1018
|
+
function writeMsalSessionCookie(account) {
|
|
1019
|
+
try {
|
|
1020
|
+
const data = encodeURIComponent(JSON.stringify({
|
|
1021
|
+
homeAccountId: account.homeAccountId,
|
|
1022
|
+
username: account.username,
|
|
1023
|
+
name: account.name ?? ""
|
|
1024
|
+
}));
|
|
1025
|
+
document.cookie = `msal.account=${data}; path=/; SameSite=Lax`;
|
|
1026
|
+
} catch {
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
function clearMsalSessionCookie() {
|
|
1030
|
+
try {
|
|
1031
|
+
document.cookie = "msal.account=; path=/; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
1032
|
+
} catch {
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1012
1035
|
function getMsalInstance() {
|
|
1013
1036
|
return globalMsalInstance;
|
|
1014
1037
|
}
|
|
@@ -1058,6 +1081,7 @@ function MsalAuthProvider({
|
|
|
1058
1081
|
}
|
|
1059
1082
|
if (response.account) {
|
|
1060
1083
|
instance.setActiveAccount(response.account);
|
|
1084
|
+
writeMsalSessionCookie(response.account);
|
|
1061
1085
|
if (config.multiTenant) {
|
|
1062
1086
|
const validation = validateTenantAccess(response.account, config.multiTenant);
|
|
1063
1087
|
if (!validation.allowed) {
|
|
@@ -1103,6 +1127,7 @@ function MsalAuthProvider({
|
|
|
1103
1127
|
const accounts = instance.getAllAccounts();
|
|
1104
1128
|
if (accounts.length > 0 && !instance.getActiveAccount()) {
|
|
1105
1129
|
instance.setActiveAccount(accounts[0]);
|
|
1130
|
+
writeMsalSessionCookie(accounts[0]);
|
|
1106
1131
|
}
|
|
1107
1132
|
const loggingEnabled = config.enableLogging || false;
|
|
1108
1133
|
instance.addEventCallback((event) => {
|
|
@@ -1111,6 +1136,7 @@ function MsalAuthProvider({
|
|
|
1111
1136
|
const account = "account" in payload ? payload.account : payload;
|
|
1112
1137
|
if (account) {
|
|
1113
1138
|
instance.setActiveAccount(account);
|
|
1139
|
+
writeMsalSessionCookie(account);
|
|
1114
1140
|
}
|
|
1115
1141
|
if (loggingEnabled) {
|
|
1116
1142
|
console.log("[MSAL] Login successful:", account?.username);
|
|
@@ -1122,10 +1148,14 @@ function MsalAuthProvider({
|
|
|
1122
1148
|
}
|
|
1123
1149
|
if (event.eventType === import_msal_browser3.EventType.LOGOUT_SUCCESS) {
|
|
1124
1150
|
instance.setActiveAccount(null);
|
|
1151
|
+
clearMsalSessionCookie();
|
|
1125
1152
|
if (loggingEnabled) {
|
|
1126
1153
|
console.log("[MSAL] Logout successful");
|
|
1127
1154
|
}
|
|
1128
1155
|
}
|
|
1156
|
+
if (import_msal_browser3.EventType.LOGOUT_END !== void 0 && event.eventType === import_msal_browser3.EventType.LOGOUT_END) {
|
|
1157
|
+
clearMsalSessionCookie();
|
|
1158
|
+
}
|
|
1129
1159
|
if (event.eventType === import_msal_browser3.EventType.ACQUIRE_TOKEN_SUCCESS) {
|
|
1130
1160
|
const payload = event.payload;
|
|
1131
1161
|
if (payload?.account && !instance.getActiveAccount()) {
|
|
@@ -1157,7 +1187,7 @@ function MsalAuthProvider({
|
|
|
1157
1187
|
if (!msalInstance) {
|
|
1158
1188
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
|
|
1159
1189
|
}
|
|
1160
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_msal_react3.MsalProvider, { instance: msalInstance, children: [
|
|
1161
1191
|
autoRefreshToken && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1162
1192
|
TokenRefreshManager,
|
|
1163
1193
|
{
|
|
@@ -1400,6 +1430,7 @@ function useGraphApi() {
|
|
|
1400
1430
|
scopes = ["User.Read"],
|
|
1401
1431
|
version = "v1.0",
|
|
1402
1432
|
debug = false,
|
|
1433
|
+
responseType = "json",
|
|
1403
1434
|
...fetchOptions
|
|
1404
1435
|
} = options;
|
|
1405
1436
|
try {
|
|
@@ -1425,9 +1456,16 @@ function useGraphApi() {
|
|
|
1425
1456
|
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
1426
1457
|
return null;
|
|
1427
1458
|
}
|
|
1428
|
-
|
|
1459
|
+
let data;
|
|
1460
|
+
if (responseType === "blob") {
|
|
1461
|
+
data = await response.blob();
|
|
1462
|
+
} else if (responseType === "text") {
|
|
1463
|
+
data = await response.text();
|
|
1464
|
+
} else {
|
|
1465
|
+
data = await response.json();
|
|
1466
|
+
}
|
|
1429
1467
|
if (debug) {
|
|
1430
|
-
console.log("[GraphAPI] Response:", data);
|
|
1468
|
+
console.log("[GraphAPI] Response:", responseType === "blob" ? "[Blob]" : data);
|
|
1431
1469
|
}
|
|
1432
1470
|
return data;
|
|
1433
1471
|
} catch (error) {
|
|
@@ -1535,11 +1573,9 @@ function useUserProfile() {
|
|
|
1535
1573
|
try {
|
|
1536
1574
|
const photoBlob = await graph.get("/me/photo/$value", {
|
|
1537
1575
|
scopes: ["User.Read"],
|
|
1538
|
-
|
|
1539
|
-
"Content-Type": "image/jpeg"
|
|
1540
|
-
}
|
|
1576
|
+
responseType: "blob"
|
|
1541
1577
|
});
|
|
1542
|
-
if (photoBlob) {
|
|
1578
|
+
if (photoBlob instanceof Blob && photoBlob.size > 0) {
|
|
1543
1579
|
photoUrl = URL.createObjectURL(photoBlob);
|
|
1544
1580
|
}
|
|
1545
1581
|
} catch (photoError) {
|
|
@@ -1927,11 +1963,11 @@ var ErrorBoundary = class extends import_react10.Component {
|
|
|
1927
1963
|
};
|
|
1928
1964
|
|
|
1929
1965
|
// src/hooks/useMultiAccount.ts
|
|
1930
|
-
var
|
|
1966
|
+
var import_msal_react4 = require("@azure/msal-react");
|
|
1931
1967
|
var import_msal_browser4 = require("@azure/msal-browser");
|
|
1932
1968
|
var import_react11 = require("react");
|
|
1933
1969
|
function useMultiAccount(defaultScopes = ["User.Read"]) {
|
|
1934
|
-
const { instance, accounts, inProgress } = (0,
|
|
1970
|
+
const { instance, accounts, inProgress } = (0, import_msal_react4.useMsal)();
|
|
1935
1971
|
const [activeAccount, setActiveAccount] = (0, import_react11.useState)(
|
|
1936
1972
|
instance.getActiveAccount()
|
|
1937
1973
|
);
|
|
@@ -3166,7 +3202,7 @@ function withPageAuth(Component2, authConfig, globalConfig) {
|
|
|
3166
3202
|
}
|
|
3167
3203
|
|
|
3168
3204
|
// src/client.ts
|
|
3169
|
-
var
|
|
3205
|
+
var import_msal_react5 = require("@azure/msal-react");
|
|
3170
3206
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3171
3207
|
0 && (module.exports = {
|
|
3172
3208
|
AccountList,
|
package/dist/index.mjs
CHANGED
|
@@ -614,6 +614,7 @@ Note: Environment variables starting with NEXT_PUBLIC_ are exposed to the browse
|
|
|
614
614
|
|
|
615
615
|
// src/hooks/useTokenRefresh.ts
|
|
616
616
|
import { useEffect, useRef, useCallback as useCallback2 } from "react";
|
|
617
|
+
import { useMsal as useMsal2 } from "@azure/msal-react";
|
|
617
618
|
|
|
618
619
|
// src/hooks/useMsalAuth.ts
|
|
619
620
|
import { useMsal, useAccount } from "@azure/msal-react";
|
|
@@ -792,7 +793,8 @@ function useTokenRefresh(options = {}) {
|
|
|
792
793
|
onRefresh,
|
|
793
794
|
onError
|
|
794
795
|
} = options;
|
|
795
|
-
const { isAuthenticated, account
|
|
796
|
+
const { isAuthenticated, account } = useMsalAuth();
|
|
797
|
+
const { instance } = useMsal2();
|
|
796
798
|
const intervalRef = useRef(null);
|
|
797
799
|
const lastRefreshRef = useRef(null);
|
|
798
800
|
const expiresInRef = useRef(null);
|
|
@@ -801,23 +803,27 @@ function useTokenRefresh(options = {}) {
|
|
|
801
803
|
return;
|
|
802
804
|
}
|
|
803
805
|
try {
|
|
804
|
-
await acquireTokenSilent(
|
|
806
|
+
const response = await instance.acquireTokenSilent({
|
|
807
|
+
scopes,
|
|
808
|
+
account,
|
|
809
|
+
forceRefresh: false
|
|
810
|
+
});
|
|
805
811
|
lastRefreshRef.current = /* @__PURE__ */ new Date();
|
|
806
|
-
const expiresIn = 3600;
|
|
812
|
+
const expiresIn = response.expiresOn ? Math.max(0, response.expiresOn.getTime() / 1e3 - Date.now() / 1e3) : 3600;
|
|
807
813
|
expiresInRef.current = expiresIn;
|
|
808
814
|
onRefresh?.(expiresIn);
|
|
809
815
|
} catch (error) {
|
|
810
816
|
console.error("[TokenRefresh] Failed to refresh token:", error);
|
|
811
817
|
onError?.(error);
|
|
812
818
|
}
|
|
813
|
-
}, [isAuthenticated, account,
|
|
819
|
+
}, [isAuthenticated, account, instance, scopes, onRefresh, onError]);
|
|
814
820
|
useEffect(() => {
|
|
815
821
|
if (!enabled || !isAuthenticated) {
|
|
816
822
|
return;
|
|
817
823
|
}
|
|
818
824
|
refresh();
|
|
819
825
|
intervalRef.current = setInterval(() => {
|
|
820
|
-
if (
|
|
826
|
+
if (expiresInRef.current === null) {
|
|
821
827
|
return;
|
|
822
828
|
}
|
|
823
829
|
const timeSinceRefresh = lastRefreshRef.current ? (Date.now() - lastRefreshRef.current.getTime()) / 1e3 : 0;
|
|
@@ -950,6 +956,23 @@ function validateTenantAccess(account, config) {
|
|
|
950
956
|
// src/components/MsalAuthProvider.tsx
|
|
951
957
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
952
958
|
var globalMsalInstance = null;
|
|
959
|
+
function writeMsalSessionCookie(account) {
|
|
960
|
+
try {
|
|
961
|
+
const data = encodeURIComponent(JSON.stringify({
|
|
962
|
+
homeAccountId: account.homeAccountId,
|
|
963
|
+
username: account.username,
|
|
964
|
+
name: account.name ?? ""
|
|
965
|
+
}));
|
|
966
|
+
document.cookie = `msal.account=${data}; path=/; SameSite=Lax`;
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function clearMsalSessionCookie() {
|
|
971
|
+
try {
|
|
972
|
+
document.cookie = "msal.account=; path=/; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
953
976
|
function getMsalInstance() {
|
|
954
977
|
return globalMsalInstance;
|
|
955
978
|
}
|
|
@@ -999,6 +1022,7 @@ function MsalAuthProvider({
|
|
|
999
1022
|
}
|
|
1000
1023
|
if (response.account) {
|
|
1001
1024
|
instance.setActiveAccount(response.account);
|
|
1025
|
+
writeMsalSessionCookie(response.account);
|
|
1002
1026
|
if (config.multiTenant) {
|
|
1003
1027
|
const validation = validateTenantAccess(response.account, config.multiTenant);
|
|
1004
1028
|
if (!validation.allowed) {
|
|
@@ -1044,6 +1068,7 @@ function MsalAuthProvider({
|
|
|
1044
1068
|
const accounts = instance.getAllAccounts();
|
|
1045
1069
|
if (accounts.length > 0 && !instance.getActiveAccount()) {
|
|
1046
1070
|
instance.setActiveAccount(accounts[0]);
|
|
1071
|
+
writeMsalSessionCookie(accounts[0]);
|
|
1047
1072
|
}
|
|
1048
1073
|
const loggingEnabled = config.enableLogging || false;
|
|
1049
1074
|
instance.addEventCallback((event) => {
|
|
@@ -1052,6 +1077,7 @@ function MsalAuthProvider({
|
|
|
1052
1077
|
const account = "account" in payload ? payload.account : payload;
|
|
1053
1078
|
if (account) {
|
|
1054
1079
|
instance.setActiveAccount(account);
|
|
1080
|
+
writeMsalSessionCookie(account);
|
|
1055
1081
|
}
|
|
1056
1082
|
if (loggingEnabled) {
|
|
1057
1083
|
console.log("[MSAL] Login successful:", account?.username);
|
|
@@ -1063,10 +1089,14 @@ function MsalAuthProvider({
|
|
|
1063
1089
|
}
|
|
1064
1090
|
if (event.eventType === EventType.LOGOUT_SUCCESS) {
|
|
1065
1091
|
instance.setActiveAccount(null);
|
|
1092
|
+
clearMsalSessionCookie();
|
|
1066
1093
|
if (loggingEnabled) {
|
|
1067
1094
|
console.log("[MSAL] Logout successful");
|
|
1068
1095
|
}
|
|
1069
1096
|
}
|
|
1097
|
+
if (EventType.LOGOUT_END !== void 0 && event.eventType === EventType.LOGOUT_END) {
|
|
1098
|
+
clearMsalSessionCookie();
|
|
1099
|
+
}
|
|
1070
1100
|
if (event.eventType === EventType.ACQUIRE_TOKEN_SUCCESS) {
|
|
1071
1101
|
const payload = event.payload;
|
|
1072
1102
|
if (payload?.account && !instance.getActiveAccount()) {
|
|
@@ -1341,6 +1371,7 @@ function useGraphApi() {
|
|
|
1341
1371
|
scopes = ["User.Read"],
|
|
1342
1372
|
version = "v1.0",
|
|
1343
1373
|
debug = false,
|
|
1374
|
+
responseType = "json",
|
|
1344
1375
|
...fetchOptions
|
|
1345
1376
|
} = options;
|
|
1346
1377
|
try {
|
|
@@ -1366,9 +1397,16 @@ function useGraphApi() {
|
|
|
1366
1397
|
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
1367
1398
|
return null;
|
|
1368
1399
|
}
|
|
1369
|
-
|
|
1400
|
+
let data;
|
|
1401
|
+
if (responseType === "blob") {
|
|
1402
|
+
data = await response.blob();
|
|
1403
|
+
} else if (responseType === "text") {
|
|
1404
|
+
data = await response.text();
|
|
1405
|
+
} else {
|
|
1406
|
+
data = await response.json();
|
|
1407
|
+
}
|
|
1370
1408
|
if (debug) {
|
|
1371
|
-
console.log("[GraphAPI] Response:", data);
|
|
1409
|
+
console.log("[GraphAPI] Response:", responseType === "blob" ? "[Blob]" : data);
|
|
1372
1410
|
}
|
|
1373
1411
|
return data;
|
|
1374
1412
|
} catch (error) {
|
|
@@ -1476,11 +1514,9 @@ function useUserProfile() {
|
|
|
1476
1514
|
try {
|
|
1477
1515
|
const photoBlob = await graph.get("/me/photo/$value", {
|
|
1478
1516
|
scopes: ["User.Read"],
|
|
1479
|
-
|
|
1480
|
-
"Content-Type": "image/jpeg"
|
|
1481
|
-
}
|
|
1517
|
+
responseType: "blob"
|
|
1482
1518
|
});
|
|
1483
|
-
if (photoBlob) {
|
|
1519
|
+
if (photoBlob instanceof Blob && photoBlob.size > 0) {
|
|
1484
1520
|
photoUrl = URL.createObjectURL(photoBlob);
|
|
1485
1521
|
}
|
|
1486
1522
|
} catch (photoError) {
|
|
@@ -1868,11 +1904,11 @@ var ErrorBoundary = class extends Component {
|
|
|
1868
1904
|
};
|
|
1869
1905
|
|
|
1870
1906
|
// src/hooks/useMultiAccount.ts
|
|
1871
|
-
import { useMsal as
|
|
1907
|
+
import { useMsal as useMsal3 } from "@azure/msal-react";
|
|
1872
1908
|
import { InteractionStatus as InteractionStatus2 } from "@azure/msal-browser";
|
|
1873
1909
|
import { useCallback as useCallback5, useMemo as useMemo2, useState as useState5, useEffect as useEffect7 } from "react";
|
|
1874
1910
|
function useMultiAccount(defaultScopes = ["User.Read"]) {
|
|
1875
|
-
const { instance, accounts, inProgress } =
|
|
1911
|
+
const { instance, accounts, inProgress } = useMsal3();
|
|
1876
1912
|
const [activeAccount, setActiveAccount] = useState5(
|
|
1877
1913
|
instance.getActiveAccount()
|
|
1878
1914
|
);
|
|
@@ -3107,7 +3143,7 @@ function withPageAuth(Component2, authConfig, globalConfig) {
|
|
|
3107
3143
|
}
|
|
3108
3144
|
|
|
3109
3145
|
// src/client.ts
|
|
3110
|
-
import { useMsal as
|
|
3146
|
+
import { useMsal as useMsal4, useIsAuthenticated, useAccount as useAccount2 } from "@azure/msal-react";
|
|
3111
3147
|
export {
|
|
3112
3148
|
AccountList,
|
|
3113
3149
|
AccountSwitcher,
|
|
@@ -3137,7 +3173,7 @@ export {
|
|
|
3137
3173
|
useAccount2 as useAccount,
|
|
3138
3174
|
useGraphApi,
|
|
3139
3175
|
useIsAuthenticated,
|
|
3140
|
-
|
|
3176
|
+
useMsal4 as useMsal,
|
|
3141
3177
|
useMsalAuth,
|
|
3142
3178
|
useMultiAccount,
|
|
3143
3179
|
useRoles,
|
package/dist/server.d.mts
CHANGED
|
@@ -41,14 +41,33 @@ interface ServerSession {
|
|
|
41
41
|
*/
|
|
42
42
|
declare function getServerSession(): Promise<ServerSession>;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Writes the `msal.account` session cookie directly via `document.cookie`.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* **Must be called from a Client Component** (browser context only).
|
|
48
|
+
*
|
|
49
|
+
* As of v5.3.0 this is no longer necessary for most apps — `MsalAuthProvider`
|
|
50
|
+
* automatically writes and clears the cookie on every login/logout event.
|
|
51
|
+
* Only call this manually if you need to set the cookie outside of the normal
|
|
52
|
+
* MSAL auth flow (e.g. after a silent SSO check in a custom component).
|
|
45
53
|
*
|
|
46
54
|
* @example
|
|
47
55
|
* ```tsx
|
|
48
|
-
*
|
|
49
|
-
*
|
|
56
|
+
* 'use client';
|
|
57
|
+
* import { setServerSessionCookie } from '@chemmangat/msal-next/server';
|
|
58
|
+
*
|
|
59
|
+
* // After a custom auth event:
|
|
60
|
+
* setServerSessionCookie(account);
|
|
50
61
|
* ```
|
|
51
62
|
*/
|
|
52
|
-
declare function setServerSessionCookie(account: any
|
|
63
|
+
declare function setServerSessionCookie(account: any): void;
|
|
64
|
+
/**
|
|
65
|
+
* Clears the `msal.account` session cookie.
|
|
66
|
+
*
|
|
67
|
+
* @remarks
|
|
68
|
+
* **Must be called from a Client Component** (browser context only).
|
|
69
|
+
* As of v5.3.0 this is handled automatically by `MsalAuthProvider` on logout.
|
|
70
|
+
*/
|
|
71
|
+
declare function clearServerSessionCookie(): void;
|
|
53
72
|
|
|
54
|
-
export { type ServerSession, getServerSession, setServerSessionCookie };
|
|
73
|
+
export { type ServerSession, clearServerSessionCookie, getServerSession, setServerSessionCookie };
|
package/dist/server.d.ts
CHANGED
|
@@ -41,14 +41,33 @@ interface ServerSession {
|
|
|
41
41
|
*/
|
|
42
42
|
declare function getServerSession(): Promise<ServerSession>;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Writes the `msal.account` session cookie directly via `document.cookie`.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* **Must be called from a Client Component** (browser context only).
|
|
48
|
+
*
|
|
49
|
+
* As of v5.3.0 this is no longer necessary for most apps — `MsalAuthProvider`
|
|
50
|
+
* automatically writes and clears the cookie on every login/logout event.
|
|
51
|
+
* Only call this manually if you need to set the cookie outside of the normal
|
|
52
|
+
* MSAL auth flow (e.g. after a silent SSO check in a custom component).
|
|
45
53
|
*
|
|
46
54
|
* @example
|
|
47
55
|
* ```tsx
|
|
48
|
-
*
|
|
49
|
-
*
|
|
56
|
+
* 'use client';
|
|
57
|
+
* import { setServerSessionCookie } from '@chemmangat/msal-next/server';
|
|
58
|
+
*
|
|
59
|
+
* // After a custom auth event:
|
|
60
|
+
* setServerSessionCookie(account);
|
|
50
61
|
* ```
|
|
51
62
|
*/
|
|
52
|
-
declare function setServerSessionCookie(account: any
|
|
63
|
+
declare function setServerSessionCookie(account: any): void;
|
|
64
|
+
/**
|
|
65
|
+
* Clears the `msal.account` session cookie.
|
|
66
|
+
*
|
|
67
|
+
* @remarks
|
|
68
|
+
* **Must be called from a Client Component** (browser context only).
|
|
69
|
+
* As of v5.3.0 this is handled automatically by `MsalAuthProvider` on logout.
|
|
70
|
+
*/
|
|
71
|
+
declare function clearServerSessionCookie(): void;
|
|
53
72
|
|
|
54
|
-
export { type ServerSession, getServerSession, setServerSessionCookie };
|
|
73
|
+
export { type ServerSession, clearServerSessionCookie, getServerSession, setServerSessionCookie };
|
package/dist/server.js
CHANGED
|
@@ -63,27 +63,29 @@ async function getServerSession() {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
function setServerSessionCookie(account) {
|
|
67
|
+
if (typeof document === "undefined") {
|
|
68
|
+
console.warn("[ServerSession] setServerSessionCookie must be called in a browser (Client Component) context.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
67
71
|
try {
|
|
68
|
-
const
|
|
72
|
+
const data = encodeURIComponent(JSON.stringify({
|
|
69
73
|
homeAccountId: account.homeAccountId,
|
|
70
74
|
username: account.username,
|
|
71
|
-
name: account.name
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
method: "POST",
|
|
75
|
-
headers: {
|
|
76
|
-
"Content-Type": "application/json"
|
|
77
|
-
},
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
account: accountData,
|
|
80
|
-
token: accessToken
|
|
81
|
-
})
|
|
82
|
-
});
|
|
75
|
+
name: account.name ?? ""
|
|
76
|
+
}));
|
|
77
|
+
document.cookie = `msal.account=${data}; path=/; SameSite=Lax`;
|
|
83
78
|
} catch (error) {
|
|
84
79
|
console.error("[ServerSession] Failed to set session cookie:", error);
|
|
85
80
|
}
|
|
86
81
|
}
|
|
82
|
+
function clearServerSessionCookie() {
|
|
83
|
+
if (typeof document === "undefined") {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
document.cookie = "msal.account=; path=/; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
87
|
+
}
|
|
87
88
|
|
|
89
|
+
exports.clearServerSessionCookie = clearServerSessionCookie;
|
|
88
90
|
exports.getServerSession = getServerSession;
|
|
89
91
|
exports.setServerSessionCookie = setServerSessionCookie;
|
package/dist/server.mjs
CHANGED
|
@@ -61,26 +61,27 @@ async function getServerSession() {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
function setServerSessionCookie(account) {
|
|
65
|
+
if (typeof document === "undefined") {
|
|
66
|
+
console.warn("[ServerSession] setServerSessionCookie must be called in a browser (Client Component) context.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
65
69
|
try {
|
|
66
|
-
const
|
|
70
|
+
const data = encodeURIComponent(JSON.stringify({
|
|
67
71
|
homeAccountId: account.homeAccountId,
|
|
68
72
|
username: account.username,
|
|
69
|
-
name: account.name
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers: {
|
|
74
|
-
"Content-Type": "application/json"
|
|
75
|
-
},
|
|
76
|
-
body: JSON.stringify({
|
|
77
|
-
account: accountData,
|
|
78
|
-
token: accessToken
|
|
79
|
-
})
|
|
80
|
-
});
|
|
73
|
+
name: account.name ?? ""
|
|
74
|
+
}));
|
|
75
|
+
document.cookie = `msal.account=${data}; path=/; SameSite=Lax`;
|
|
81
76
|
} catch (error) {
|
|
82
77
|
console.error("[ServerSession] Failed to set session cookie:", error);
|
|
83
78
|
}
|
|
84
79
|
}
|
|
80
|
+
function clearServerSessionCookie() {
|
|
81
|
+
if (typeof document === "undefined") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
document.cookie = "msal.account=; path=/; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
85
|
+
}
|
|
85
86
|
|
|
86
|
-
export { getServerSession, setServerSessionCookie };
|
|
87
|
+
export { clearServerSessionCookie, getServerSession, setServerSessionCookie };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chemmangat/msal-next",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"description": "Production-ready Microsoft/Azure AD authentication for Next.js App Router. Zero-config setup, TypeScript-first, multi-account support, auto token refresh. The easiest way to add Microsoft login to your Next.js app.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|