Hi all,
Calls to our backend require the Authorization header to be filled with an access token. We do this using EventStore's onBeforeRequest
callback.
An issue we're running into, is that the token can't be retrieved synchronously; our auth mechanism (MSAL) usually needs to do an AJAX call to refresh the token.
Therefore it would be nice if onBeforeRequest accepted a Promise as its return value, so any backend calls triggered by EventStore will wait until the Promise (i.e. the token) is resolved.
See below how we currently work around it. It's not ideal, as it doesn't play nice with hot reloads. We can maybe use a ref to further work around that.
Questions:
- Is there already a better way to do this?
- @ Bryntum: can onBeforeRequest be changed to accept a Promise?
Thanks,
Roberto
const MyScheduler = ({ authToken }: { authToken: string }) => {
const onBeforeRequest = useCallback(
(event: any) => {
(event.source as AjaxStore).setConfig({
headers: { Authorization: `Bearer ${authToken}` },
});
return event;
},
[authToken]
);
const eventStore = useMemo(
() =>
new EventStore({
// ...
onBeforeRequest,
}),
[onBeforeRequest]
);
// Memoized to avoid re-rendering when our custom modal opens
const schedulerConfig: BryntumSchedulerProps = useMemo(() => {
return {
eventStore,
};
}, [eventStore]);
return (
<>
<BryntumScheduler {...schedulerConfig} />
</>
);
};
/**
* Hook which intends to provide a synchronous way to
* get a valid MSAL (oAuth) token at all times,
* dynamically updating in the background.
*/
function useMsalToken() {
const { instance } = useMsal();
const [token, setToken] = useState<string | undefined>(undefined);
const acquireToken = useCallback(async () => {
const account = instance.getActiveAccount();
if (!account) return;
const request = { scopes: msalScopes, account };
try {
const response = await instance.acquireTokenSilent(request);
setToken(response.idToken);
} catch (error) {
console.error("useMsalToken error: ", error);
}
}, [instance]);
useEffect(() => {
acquireToken().catch((e) => {
console.error("Error acquiring token in background", e);
});
const intervalId = window.setInterval(acquireToken, 1000 * 60); // Refresh every minute
return () => window.clearInterval(intervalId);
}, [acquireToken]);
return token;
}
const MsalTokenWrapper = ({
children,
}: {
children: (data: { token: string }) => React.ReactNode;
}) => {
const token = useMsalToken();
if (!token) return <>Waiting for auth token...</>;
return children({ token });
};
const MySchedulerWrapped = (
<MsalTokenWrapper>
{({ token }) => <MyScheduler authToken={token} />}
</MsalTokenWrapper>
);