@joouis/msal-react-utility
Version:
The utility package for @azure/msal-react.
238 lines (226 loc) • 7.87 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
RequestInProgressError: () => RequestInProgressError,
TokenType: () => TokenType,
getResponseData: () => getResponseData,
sleep: () => sleep,
useEventCallback: () => useEventCallback,
useFetchWithStatus: () => useFetchWithStatus,
useFetchWithToken: () => useFetchWithToken,
useGetToken: () => useGetToken
});
module.exports = __toCommonJS(src_exports);
// src/hooks/useEventCallback.ts
var import_react = require("react");
function useEventCallback(handler) {
const handlerRef = (0, import_react.useRef)(handler);
(0, import_react.useLayoutEffect)(() => {
handlerRef.current = handler;
});
return (0, import_react.useCallback)((...args) => {
const handle = handlerRef.current;
return handle(...args);
}, []);
}
// src/hooks/useFetchWithStatus.ts
var import_react4 = __toESM(require("react"), 1);
// src/hooks/useFetchWithToken.ts
var import_react3 = require("react");
// src/hooks/useGetToken.ts
var import_react2 = require("react");
var import_msal_react = require("@azure/msal-react");
var import_msal_browser = require("@azure/msal-browser");
// src/utilities/sleep.ts
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// src/inteface.ts
var TokenType = /* @__PURE__ */ ((TokenType2) => {
TokenType2["id"] = "id";
TokenType2["access"] = "access";
return TokenType2;
})(TokenType || {});
var RequestInProgressError = class extends Error {
constructor() {
super("Request is in progress!");
}
};
// src/hooks/useGetToken.ts
var useGetToken = (defaultRequestConfigs) => {
const { instance, inProgress, accounts } = (0, import_msal_react.useMsal)();
const inProgressRef = (0, import_react2.useRef)(inProgress);
const account = accounts[0];
(0, import_react2.useEffect)(() => {
inProgressRef.current = inProgress;
}, [inProgress]);
const getToken = useEventCallback(async (opts) => {
const { tokenType = "access" /* access */, requestConfigs } = opts || {};
while (inProgressRef.current !== import_msal_browser.InteractionStatus.None) {
await sleep(100);
}
const configs = {
scopes: ["User.Read"],
prompt: "select_account",
...defaultRequestConfigs,
...requestConfigs
};
try {
const activeAccount = instance.getActiveAccount() || account;
if (!activeAccount) {
await instance.loginRedirect();
}
const resp = await instance.acquireTokenSilent({
account: activeAccount,
...configs
});
if (tokenType === "access") {
return resp.accessToken;
}
if (!resp.idToken) {
throw new Error("ID token is not available");
}
const idTokenExp = resp.idTokenClaims.exp;
if (resp.fromCache && idTokenExp * 1e3 - Date.now() < 2 * 60 * 1e3) {
return await getToken({
tokenType: "id" /* id */,
requestConfigs: { ...configs, forceRefresh: true }
});
}
return resp.idToken;
} catch (error) {
console.error(`[getToken] ${error}`);
if (error instanceof import_msal_browser.InteractionRequiredAuthError) {
await instance.acquireTokenRedirect(configs);
} else if (error instanceof import_msal_browser.BrowserAuthError && error.errorCode === import_msal_browser.BrowserAuthErrorCodes.interactionInProgress) {
await sleep(12e4);
} else {
throw error;
}
}
});
return getToken;
};
// src/hooks/useFetchWithToken.ts
var useFetchWithToken = (tokenRequestConfigs) => {
const getToken = useGetToken(tokenRequestConfigs);
return (0, import_react3.useCallback)(
async (input, init, getTokenOpts) => {
try {
const token = await getToken(getTokenOpts);
if (!token) {
throw new Error("Failed to fetch token");
}
return fetch(input, {
...init,
headers: {
Authorization: `Bearer ${token}`,
// User can override token
...init?.headers
}
});
} catch (error) {
console.error(`[useFetchWithToken] ${error}`);
const { name, message } = error;
return new Response(name, {
status: 401,
statusText: message
});
}
},
[getToken]
);
};
// src/utilities/getResponseData.ts
var getResponseData = async (response) => {
let data;
const contentType = response.headers.get("Content-Type") || "";
if (contentType.includes("application/json")) {
data = await response.json();
} else if (contentType.includes("text/")) {
data = await response.text();
} else if (contentType.includes("application/octet-stream")) {
data = await response.arrayBuffer();
} else if (contentType.includes("application/xml") || contentType.includes("text/xml")) {
data = await response.text();
} else if (contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/") || contentType.includes("application/pdf")) {
data = await response.blob();
} else {
try {
data = await response.json();
} catch (e) {
data = await response.text();
}
}
return data;
};
// src/hooks/useFetchWithStatus.ts
var useFetchWithStatus = (input, init) => {
const [isLoading, setIsLoading] = import_react4.default.useState(false);
const isLoadingRef = import_react4.default.useRef(false);
const fetchWithToken = useFetchWithToken();
const _fetch = useEventCallback(async (payload, getTokenOpts) => {
if (isLoadingRef.current) {
throw new RequestInProgressError();
}
setIsLoading(true);
isLoadingRef.current = true;
const requestInit = !init && !payload ? void 0 : { ...init, ...payload };
try {
const response = await fetchWithToken(input, requestInit, getTokenOpts);
if (!response.ok) {
throw new Error(`Failed to fetch data: ${response.statusText}`, {
cause: response
});
}
const data = await getResponseData(response);
return data;
} catch (error) {
throw error;
} finally {
setIsLoading(false);
isLoadingRef.current = false;
}
});
return {
isLoading,
_fetch
};
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
RequestInProgressError,
TokenType,
getResponseData,
sleep,
useEventCallback,
useFetchWithStatus,
useFetchWithToken,
useGetToken
});