Skip to content

Commit

Permalink
fix: refactor getAccessToken to prevent async issues (#69)
Browse files Browse the repository at this point in the history
* fix: refactor getAccessToken to prevent async issues

* Update bundlesize.config.js
  • Loading branch information
aversini authored Jul 5, 2024
1 parent 6639cf9 commit 7edb48b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 18 deletions.
2 changes: 1 addition & 1 deletion packages/auth-provider/bundlesize.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default {
*/
{
path: "dist/index.js",
limit: "10 kb",
limit: "11 kb",
},
],
};
84 changes: 84 additions & 0 deletions packages/auth-provider/src/common/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,87 @@ export const getAccessTokenSilently = async ({
};
}
};

type RefreshTokenResponse = {
status: "success" | "failure";
newAccessToken?: string;
newRefreshToken?: string;
};
type RefreshTokenProps = {
clientId: string;
userId: string;
nonce: string;
};
export class TokenManager {
private refreshTokenPromise: Promise<any> | null = null;
private accessToken: string;
private refreshToken: string;

constructor(
accessToken: string | null = null,
refreshToken: string | null = null,
) {
this.accessToken = accessToken || "";
this.refreshToken = refreshToken || "";
}

async refreshtoken({
clientId,
userId,
nonce,
}: RefreshTokenProps): Promise<RefreshTokenResponse> {
if (!this.refreshTokenPromise) {
// No existing refresh in progress, start a new one
this.refreshTokenPromise = this._refreshToken({
clientId,
userId,
nonce,
});
}

try {
// Wait for the existing refresh or start a new one
return await this.refreshTokenPromise;
} finally {
// Clear the promise to allow subsequent calls
this.refreshTokenPromise = null;
}
}

private async _refreshToken({
clientId,
userId,
nonce,
}: RefreshTokenProps): Promise<RefreshTokenResponse> {
const jwtRefresh = await verifyAndExtractToken(this.refreshToken);
if (jwtRefresh && jwtRefresh.payload[JWT.USER_ID_KEY] !== "") {
// Refresh token is valid, refreshing access token...
const response = await getAccessTokenSilently({
clientId,
userId,
nonce,
refreshToken: this.refreshToken,
accessToken: this.accessToken,
});
if (response.status) {
this.accessToken = response.accessToken;
this.refreshToken = response.refreshToken;
return {
status: "success",
newAccessToken: response.accessToken,
newRefreshToken: response.refreshToken,
};
} else {
// Access token could not be refreshed, re-authenticate the user...
return {
status: "failure",
};
}
} else {
// Refresh token is not valid, re-authenticate the user...
return {
status: "failure",
};
}
}
}
28 changes: 11 additions & 17 deletions packages/auth-provider/src/components/AuthProvider/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import type {
LoginType,
} from "../../common/types";
import {
TokenManager,
authenticateUser,
getAccessTokenSilently,
getPreAuthCode,
logoutUser,
} from "../../common/utilities";
Expand All @@ -47,6 +47,7 @@ export const AuthProvider = ({
const [nonce, setNonce, , removeNonce] = useLocalStorage({
key: `${LOCAL_STORAGE_PREFIX}::${clientId}::@@nonce@@`,
});
const tokenManager = new TokenManager(accessToken, refreshToken);

const [authState, setAuthState] = useState<AuthState>({
isLoading: true,
Expand Down Expand Up @@ -216,22 +217,15 @@ export const AuthProvider = ({
* accessToken is not valid, so we need to try to refresh it using the
* refreshToken - this is a silent refresh.
*/
const jwtRefresh = await verifyAndExtractToken(refreshToken);
if (jwtRefresh && jwtRefresh.payload[JWT.USER_ID_KEY] !== "") {
const response = await getAccessTokenSilently({
clientId,
userId: user.userId as string,
nonce,
refreshToken,
accessToken,
});
if (response.status) {
setAccessToken(response.accessToken);
setRefreshToken(response.refreshToken);
return response.accessToken;
}
removeStateAndLocalStorage(ACCESS_TOKEN_ERROR);
return "";
const res = await tokenManager.refreshtoken({
clientId,
userId: user.userId as string,
nonce,
});
if (res.status) {
setAccessToken(res.newAccessToken);
setRefreshToken(res.newRefreshToken);
return res.newAccessToken;
}
/**
* refreshToken is not valid, so we need to re-authenticate the user.
Expand Down

0 comments on commit 7edb48b

Please sign in to comment.