Skip to content

Commit 1f1d57e

Browse files
committedOct 28, 2024··
refactor(web): optimize-session-invalidation
1 parent 7b2ccd3 commit 1f1d57e

File tree

4 files changed

+73
-20
lines changed

4 files changed

+73
-20
lines changed
 

‎web/src/context/AtlasProvider.tsx

+45-12
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import {
2222
type ConfirmEmailResponse,
2323
Roles,
2424
Products,
25+
AuthorizationError,
2526
} from "utils/atlas";
2627

2728
import { isUndefined } from "src/utils";
29+
import { GraphQLError } from "graphql";
2830

2931
interface IAtlasProvider {
3032
isVerified: boolean;
@@ -94,16 +96,32 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
9496
}, [authToken, address]);
9597

9698
useEffect(() => {
97-
// initial verfiy check
98-
setIsVerified(verifySession());
99+
let timeoutId: NodeJS.Timeout;
99100

100-
// verify session every 5 sec
101-
const intervalId = setInterval(() => {
102-
setIsVerified(verifySession());
103-
}, 5000);
101+
const verifyAndSchedule = () => {
102+
console.log("checking");
103+
104+
// initial verfiy check
105+
const isValid = verifySession();
106+
setIsVerified(isValid);
107+
108+
if (isValid && authToken) {
109+
try {
110+
const payload = decodeJwt(authToken);
111+
const expiresIn = (payload.exp as number) * 1000 - Date.now();
112+
113+
timeoutId = setTimeout(verifyAndSchedule, Math.max(0, expiresIn));
114+
} catch (err) {
115+
console.error("Error decoding JWT:", err);
116+
setIsVerified(false);
117+
}
118+
}
119+
};
120+
121+
verifyAndSchedule();
104122

105123
return () => {
106-
clearInterval(intervalId);
124+
clearTimeout(timeoutId);
107125
};
108126
}, [authToken, verifySession, address]);
109127

@@ -140,6 +158,21 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
140158
return !isUndefined(user.email);
141159
}, [user]);
142160

161+
async function fetchWithAuthErrorHandling<T>(request: () => Promise<T>): Promise<T> {
162+
try {
163+
return await request();
164+
} catch (error) {
165+
if (
166+
error instanceof AuthorizationError ||
167+
(error instanceof GraphQLError && error.extensions["code"] === "UNAUTHENTICATED")
168+
) {
169+
setIsVerified(false);
170+
throw error;
171+
}
172+
throw error;
173+
}
174+
}
175+
143176
/**
144177
* @description authorise user and enable authorised calls
145178
*/
@@ -173,7 +206,7 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
173206
if (!address || !isVerified) return false;
174207
setIsAddingUser(true);
175208

176-
const userAdded = await addUserToAtlas(atlasGqlClient, userSettings);
209+
const userAdded = await fetchWithAuthErrorHandling(() => addUserToAtlas(atlasGqlClient, userSettings));
177210
refetchUser();
178211

179212
return userAdded;
@@ -199,7 +232,8 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
199232
if (!address || !isVerified) return false;
200233
setIsUpdatingUser(true);
201234

202-
const emailUpdated = await updateEmailInAtlas(atlasGqlClient, userSettings);
235+
// const emailUpdated = await updateEmailInAtlas(atlasGqlClient, userSettings);
236+
const emailUpdated = await fetchWithAuthErrorHandling(() => updateEmailInAtlas(atlasGqlClient, userSettings));
203237
refetchUser();
204238

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

230-
const hash = await uploadToIpfs(
231-
{ baseUrl: atlasUri, authToken },
232-
{ file, name: file.name, role, product: Products.CourtV2 }
264+
const hash = await fetchWithAuthErrorHandling(() =>
265+
uploadToIpfs({ baseUrl: atlasUri, authToken }, { file, name: file.name, role, product: Products.CourtV2 })
233266
);
234267
return hash ? `/ipfs/${hash}` : null;
235268
} catch (err: any) {

‎web/src/utils/atlas/addUser.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GraphQLError } from "graphql";
12
import { gql, type GraphQLClient } from "graphql-request";
23
import { toast } from "react-toastify";
34

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

33-
const errorMessage = Array.isArray(errors?.response?.errors)
34-
? errors.response.errors[0]?.message
35-
: "Unknown error";
36-
throw new Error(errorMessage);
34+
const error = errors?.response?.errors?.[0];
35+
36+
if (error) {
37+
throw new GraphQLError(error?.message, { ...error });
38+
}
39+
throw new Error("Unknown Error");
3740
}),
3841
{
3942
pending: `Adding User ...`,

‎web/src/utils/atlas/updateEmail.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GraphQLError } from "graphql";
12
import { gql, type GraphQLClient } from "graphql-request";
23
import { toast } from "react-toastify";
34

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

31-
const errorMessage = Array.isArray(errors?.response?.errors)
32-
? errors.response.errors[0]?.message
33-
: "Unknown error";
34-
throw new Error(errorMessage);
32+
const error = errors?.response?.errors?.[0];
33+
34+
if (error) {
35+
throw new GraphQLError(error?.message, { ...error });
36+
}
37+
throw new Error("Unknown Error");
3538
}),
3639
{
3740
pending: `Updating Email ...`,

‎web/src/utils/atlas/uploadToIpfs.ts

+14
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export async function uploadToIpfs(config: Config, payload: IpfsUploadPayload):
4242
}).then(async (response) => {
4343
if (!response.ok) {
4444
const error = await response.json().catch(() => ({ message: "Error uploading to IPFS" }));
45+
46+
if (response.status === 401) throw new AuthorizationError(error.message);
47+
4548
throw new Error(error.message);
4649
}
4750

@@ -59,3 +62,14 @@ export async function uploadToIpfs(config: Config, payload: IpfsUploadPayload):
5962
OPTIONS
6063
);
6164
}
65+
66+
export class AuthorizationError extends Error {
67+
constructor(message: string) {
68+
super(message);
69+
this.name = "AuthorizationError";
70+
71+
if (Error.captureStackTrace) {
72+
Error.captureStackTrace(this, this.constructor);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)
Please sign in to comment.