Skip to content

Commit

Permalink
Merge pull request #37 from bento-platform/fix/refresh-tokens-spam
Browse files Browse the repository at this point in the history
Don't execute invalid refresh token attempts
  • Loading branch information
davidlougheed authored Sep 13, 2024
2 parents ce81773 + 24aebeb commit aa754c3
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
19 changes: 17 additions & 2 deletions src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MutableRefObject, useCallback, useEffect, useMemo } from "react";
import { MutableRefObject, useCallback, useEffect, useMemo, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AnyAction } from "redux";
import { ThunkAction } from "redux-thunk";
Expand Down Expand Up @@ -135,14 +135,29 @@ export const useSessionWorkerTokenRefresh = (
const dispatch: AppDispatch = useDispatch();
const { clientId } = useBentoAuthContext();

const { refreshToken } = useAuthState();

const refreshTokenRef = useRef<string | undefined>(refreshToken);

useEffect(() => {
// A bit hacky: we use a ref to get the refreshToken into the worker event listener without triggering a
// dependency change for the useEffect below.
refreshTokenRef.current = refreshToken;
}, [refreshToken]);

useEffect(() => {
if (!clientId) {
logMissingAuthContext("clientId");
} else {
if (!sessionWorkerRef.current) {
const sw = createWorker();
sw.addEventListener("message", () => {
dispatch(refreshTokens(clientId));
// It would be nice to check if we have a refresh token here without refs, but doing so would mean
// unbinding and re-binding the listener every time the effect is re-executed. Instead, we can use a
// ref to access the token without triggering a hook dependency change.
// While the action itself also handles the no refresh token case, it pollutes the Redux and console
// logs and so it's nicer to re-check here.
if (refreshTokenRef.current) dispatch(refreshTokens(clientId));
dispatch(fetchUserDependentData);
});
sessionWorkerRef.current = sw;
Expand Down
10 changes: 9 additions & 1 deletion src/redux/authSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export const refreshTokens = createAsyncThunk<RefreshTokenPayload, string>(

if (!url) return;

const { refreshToken } = state.auth;

if (!refreshToken) {
// We shouldn't execute a request that we know will fail. If no refresh token is set, this action errors -
// the user is (should already be) signed out with no permissions.
throw new Error("No refresh token present"); // Throw an error to definitively reset auth slice state.
}

const response = await fetch(url, {
method: "POST",
headers: {
Expand All @@ -81,7 +89,7 @@ export const refreshTokens = createAsyncThunk<RefreshTokenPayload, string>(
body: buildUrlEncodedData({
grant_type: "refresh_token",
client_id: clientId,
refresh_token: state.auth.refreshToken,
refresh_token: refreshToken,
}),
});

Expand Down

0 comments on commit aa754c3

Please sign in to comment.