Skip to content

Commit

Permalink
add allowable clock skew to willAuthError
Browse files Browse the repository at this point in the history
(cherry picked from commit e32e291)
  • Loading branch information
petertgiles authored and brindasasi committed Jan 6, 2025
1 parent eb85571 commit fcff04f
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,40 @@ import isEqual from "lodash/isEqual";
import { extractErrorMessages } from "../../utils/errors";
import { exportedForTesting } from "./ClientProvider";

const { isTokenKnownToBeExpired } = exportedForTesting;
const { isTokenProbablyExpired } = exportedForTesting;

describe("ClientProvider tests", () => {
// some API requests do not require auth to succeed
test("If there is no auth then willAuthError is false", () => {
const accessToken = null;
const result = isTokenKnownToBeExpired(accessToken);
const result = isTokenProbablyExpired(accessToken);
expect(result).toEqual(false);
});

test("If there is an accessToken that has not expired then willAuthError is false", () => {
const accessToken =
"eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjIxNDc0ODM2NDcsImlhdCI6MH0.v5o7sfcTiqB21JrCZ1ytP0gJp4JeTuiEdO8yVBVro7Y"; // expires Jan 18 2038
const result = isTokenKnownToBeExpired(accessToken);
const result = isTokenProbablyExpired(accessToken);
expect(result).toEqual(false);
});

test("If there is an accessToken that has expired then willAuthError is true", () => {
const accessToken =
"eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjEsImlhdCI6MH0.d5nyMUDCvbvfTmg3ow_cN4YZX4jmfoXAGq3DbCw5LAc"; // expires Dec 31 1969
const result = isTokenKnownToBeExpired(accessToken);
const result = isTokenProbablyExpired(accessToken);
expect(result).toEqual(true);
});

test("If there is an accessToken that has not expired but is within allowable skew then willAuthError is true", () => {
// 10 seconds before the token expires
jest.useFakeTimers().setSystemTime((2147483647 - 10) * 1000);
const accessToken =
"eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjIxNDc0ODM2NDcsImlhdCI6MH0.v5o7sfcTiqB21JrCZ1ytP0gJp4JeTuiEdO8yVBVro7Y"; // expires Jan 18 2038 (2147483647)
const result = isTokenProbablyExpired(accessToken);
expect(result).toEqual(true);
jest.useRealTimers();
});

test("finds validation errors", () => {
// an error object for testing on
// sorry, very ugly - would be nice to use stubs but I haven't figured that out
Expand Down
17 changes: 10 additions & 7 deletions packages/client/src/components/ClientProvider/ClientProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,21 @@ import {
} from "../../utils/errors";
import specialErrorExchange from "../../exchanges/specialErrorExchange";
import protectedEndpointExchange from "../../exchanges/protectedEndpointExchange";
import { apiHost, apiUri } from "../../constants";
import { allowableClockSkewSeconds, apiHost, apiUri } from "../../constants";

const isTokenKnownToBeExpired = (accessToken: string | null): boolean => {
let tokenIsKnownToBeExpired = false;
const isTokenProbablyExpired = (accessToken: string | null): boolean => {
let tokenProbablyExpired = false;
if (accessToken) {
const decoded = jwtDecode<JwtPayload>(accessToken);
if (decoded.exp) {
tokenIsKnownToBeExpired = Date.now() > decoded.exp * 1000; // JWT expiry date in seconds, not milliseconds
const tokenExpiryDateSeconds = decoded.exp;
const safeTokenExpiryDateSeconds =
tokenExpiryDateSeconds - allowableClockSkewSeconds; // allow for the client's machine to be a bit off
tokenProbablyExpired = Date.now() > safeTokenExpiryDateSeconds * 1000; // JWT expiry date in seconds to milliseconds
}
}

return tokenIsKnownToBeExpired;
return tokenProbablyExpired;
};

const ClientProvider = ({
Expand Down Expand Up @@ -141,7 +144,7 @@ const ClientProvider = ({
},
willAuthError() {
const accessToken = localStorage.getItem(ACCESS_TOKEN);
return isTokenKnownToBeExpired(accessToken);
return isTokenProbablyExpired(accessToken);
},
didAuthError(error) {
const res = error.response as Response | null;
Expand Down Expand Up @@ -179,5 +182,5 @@ export default ClientProvider;

// https://stackoverflow.com/questions/54116070/how-can-i-unit-test-non-exported-functions
export const exportedForTesting = {
isTokenKnownToBeExpired,
isTokenProbablyExpired,
};
1 change: 1 addition & 0 deletions packages/client/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export const protectedUrl =
typeof API_PROTECTED_URI !== "undefined"
? API_PROTECTED_URI
: "/admin/graphql";
export const allowableClockSkewSeconds = 20;

0 comments on commit fcff04f

Please sign in to comment.