Skip to content

Commit

Permalink
Merge pull request #1727 from kleros/refactor/optimize-session-invali…
Browse files Browse the repository at this point in the history
…dation

refactor(web): optimize-session-invalidation
  • Loading branch information
alcercu authored Oct 29, 2024
2 parents d1a6020 + a859958 commit 01673c3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 20 deletions.
53 changes: 41 additions & 12 deletions web/src/context/AtlasProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import {
type ConfirmEmailResponse,
Roles,
Products,
AuthorizationError,
} from "utils/atlas";

import { isUndefined } from "src/utils";
import { GraphQLError } from "graphql";

interface IAtlasProvider {
isVerified: boolean;
Expand Down Expand Up @@ -94,16 +96,30 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
}, [authToken, address]);

useEffect(() => {
// initial verfiy check
setIsVerified(verifySession());
let timeoutId: ReturnType<typeof setTimeout>;

const verifyAndSchedule = () => {
// initial verify check
const isValid = verifySession();
setIsVerified(isValid);

if (isValid && authToken) {
try {
const payload = decodeJwt(authToken);
const expiresIn = (payload.exp as number) * 1000 - Date.now();

timeoutId = setTimeout(verifyAndSchedule, Math.max(0, expiresIn));
} catch (err) {
console.error("Error decoding JWT:", err);
setIsVerified(false);
}
}
};

// verify session every 5 sec
const intervalId = setInterval(() => {
setIsVerified(verifySession());
}, 5000);
verifyAndSchedule();

return () => {
clearInterval(intervalId);
clearTimeout(timeoutId);
};
}, [authToken, verifySession, address]);

Expand Down Expand Up @@ -140,6 +156,20 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
return !isUndefined(user.email);
}, [user]);

async function fetchWithAuthErrorHandling<T>(request: () => Promise<T>): Promise<T> {
try {
return await request();
} catch (error) {
if (
error instanceof AuthorizationError ||
(error instanceof GraphQLError && error?.extensions?.["code"] === "UNAUTHENTICATED")
) {
setIsVerified(false);
}
throw error;
}
}

/**
* @description authorise user and enable authorised calls
*/
Expand Down Expand Up @@ -173,7 +203,7 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
if (!address || !isVerified) return false;
setIsAddingUser(true);

const userAdded = await addUserToAtlas(atlasGqlClient, userSettings);
const userAdded = await fetchWithAuthErrorHandling(() => addUserToAtlas(atlasGqlClient, userSettings));
refetchUser();

return userAdded;
Expand All @@ -199,7 +229,7 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
if (!address || !isVerified) return false;
setIsUpdatingUser(true);

const emailUpdated = await updateEmailInAtlas(atlasGqlClient, userSettings);
const emailUpdated = await fetchWithAuthErrorHandling(() => updateEmailInAtlas(atlasGqlClient, userSettings));
refetchUser();

return emailUpdated;
Expand Down Expand Up @@ -227,9 +257,8 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
if (!address || !isVerified || !atlasUri || !authToken) return null;
setIsUploadingFile(true);

const hash = await uploadToIpfs(
{ baseUrl: atlasUri, authToken },
{ file, name: file.name, role, product: Products.CourtV2 }
const hash = await fetchWithAuthErrorHandling(() =>
uploadToIpfs({ baseUrl: atlasUri, authToken }, { file, name: file.name, role, product: Products.CourtV2 })
);
return hash ? `/ipfs/${hash}` : null;
} catch (err: any) {
Expand Down
11 changes: 7 additions & 4 deletions web/src/utils/atlas/addUser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GraphQLError } from "graphql";
import { gql, type GraphQLClient } from "graphql-request";
import { toast } from "react-toastify";

Expand Down Expand Up @@ -30,10 +31,12 @@ export function addUser(client: GraphQLClient, userData: AddUserData): Promise<b
// eslint-disable-next-line no-console
console.log("Add User error:", { errors });

const errorMessage = Array.isArray(errors?.response?.errors)
? errors.response.errors[0]?.message
: "Unknown error";
throw new Error(errorMessage);
const error = errors?.response?.errors?.[0];

if (error) {
throw new GraphQLError(error?.message, { ...error });
}
throw new Error("Unknown Error");
}),
{
pending: `Adding User ...`,
Expand Down
11 changes: 7 additions & 4 deletions web/src/utils/atlas/updateEmail.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GraphQLError } from "graphql";
import { gql, type GraphQLClient } from "graphql-request";
import { toast } from "react-toastify";

Expand Down Expand Up @@ -28,10 +29,12 @@ export function updateEmail(client: GraphQLClient, userData: UpdateEmailData): P
// eslint-disable-next-line no-console
console.log("Update Email error:", { errors });

const errorMessage = Array.isArray(errors?.response?.errors)
? errors.response.errors[0]?.message
: "Unknown error";
throw new Error(errorMessage);
const error = errors?.response?.errors?.[0];

if (error) {
throw new GraphQLError(error?.message, { ...error });
}
throw new Error("Unknown Error");
}),
{
pending: `Updating Email ...`,
Expand Down
13 changes: 13 additions & 0 deletions web/src/utils/atlas/uploadToIpfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export async function uploadToIpfs(config: Config, payload: IpfsUploadPayload):
}).then(async (response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: "Error uploading to IPFS" }));

if (response.status === 401) throw new AuthorizationError(error.message);
throw new Error(error.message);
}

Expand All @@ -59,3 +61,14 @@ export async function uploadToIpfs(config: Config, payload: IpfsUploadPayload):
OPTIONS
);
}

export class AuthorizationError extends Error {
readonly name = "AuthorizationError" as const;
constructor(message: string) {
super(message);

if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}

0 comments on commit 01673c3

Please sign in to comment.