Skip to content

Commit 67d19c1

Browse files
moshloopadityathebe
authored andcommitted
fix(auth): add login timeout and error handling
1 parent 3816e94 commit 67d19c1

3 files changed

Lines changed: 43 additions & 28 deletions

File tree

pages/api/auth/login.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,35 @@ export default async function handler(
1313
const { username, password } = req.body;
1414
const basicAuth = Buffer.from(`${username}:${password}`).toString("base64");
1515

16-
const response = await fetch(`${BACKEND_URL}/auth/login`, {
17-
method: "POST",
18-
headers: {
19-
"Content-Type": "application/json",
20-
Authorization: `Basic ${basicAuth}`
21-
},
22-
body: JSON.stringify(req.body)
23-
});
16+
const controller = new AbortController();
17+
const timeout = setTimeout(() => controller.abort(), 10_000);
2418

25-
const setCookie = response.headers.get("set-cookie");
26-
if (setCookie) {
27-
res.setHeader("set-cookie", setCookie);
28-
}
19+
try {
20+
const response = await fetch(`${BACKEND_URL}/auth/login`, {
21+
method: "POST",
22+
headers: {
23+
"Content-Type": "application/json",
24+
Authorization: `Basic ${basicAuth}`
25+
},
26+
body: JSON.stringify(req.body),
27+
signal: controller.signal
28+
});
29+
30+
const setCookie = response.headers.get("set-cookie");
31+
if (setCookie) {
32+
res.setHeader("set-cookie", setCookie);
33+
}
2934

30-
const text = await response.text();
31-
res.status(response.status);
32-
res.send(text);
35+
const text = await response.text();
36+
res.status(response.status).send(text);
37+
} catch (error: any) {
38+
if (error.name === "AbortError") {
39+
return res.status(504).json({ error: "Login request timed out" });
40+
}
41+
return res
42+
.status(502)
43+
.json({ error: "Failed to connect to authentication service" });
44+
} finally {
45+
clearTimeout(timeout);
46+
}
3347
}

src/api/axios.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,18 +244,20 @@ for (const client of [
244244

245245
export function redirectToLoginPageOnSessionExpiry(error: AxiosError) {
246246
if (error?.response?.status === 401) {
247+
const returnTo = encodeURIComponent(
248+
`${window.location.pathname}${window.location.search}`
249+
);
250+
247251
if (isBasicAuthSystem) {
248-
window.location.href = `/login?return_to=${window.location.pathname}${window.location.search}`;
252+
window.location.href = `/login?return_to=${returnTo}`;
249253
return;
250254
}
251255

252256
if (isClerkAuthSystem) {
253-
const url = `/auth-state-checker?return_to=${window.location.pathname}${window.location.search}`;
254-
window.location.href = url;
257+
window.location.href = `/auth-state-checker?return_to=${returnTo}`;
255258
return;
256259
}
257260

258-
const url = `/login?return_to=${window.location.pathname}${window.location.search}`;
259-
window.location.href = url;
261+
window.location.href = `/login?return_to=${returnTo}`;
260262
}
261263
}

src/components/Authentication/Kratos/KratosLogin.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const KratosLogin = () => {
3333
const searchParams = useSearchParams();
3434

3535
const flowId = searchParams.get("flow") || undefined;
36-
const returnTo = searchParams.get("return_to") || "/";
36+
const returnTo = searchParams.get("return_to") || undefined;
3737
const username = searchParams.get("username");
3838
const password = searchParams.get("password");
3939

@@ -54,7 +54,7 @@ const KratosLogin = () => {
5454
const { data } = await ory.getLoginFlow({ id });
5555
setFlow(data);
5656
} catch (error) {
57-
handleError(error as AxiosError);
57+
return handleError(error as AxiosError);
5858
}
5959
// eslint-disable-next-line react-hooks/exhaustive-deps
6060
}, []);
@@ -68,13 +68,13 @@ const KratosLogin = () => {
6868
);
6969

7070
const createFlow = useCallback(
71-
async (refresh: boolean, aal: string, returnTo: string = "/") => {
71+
async (refresh: boolean, aal: string, returnTo?: string) => {
7272
try {
7373
const { data } = await ory.createBrowserLoginFlow({
7474
refresh: refresh,
7575
// Check for two-factor authentication
7676
aal: aal,
77-
returnTo: returnTo
77+
...(returnTo ? { returnTo } : {})
7878
});
7979
setFlow(data);
8080
if (flowId !== data.id) {
@@ -97,13 +97,13 @@ const KratosLogin = () => {
9797

9898
if (flowId) {
9999
getFlow(flowId).catch(() => {
100-
createFlow(refresh, aal, String(returnTo ?? "/"));
100+
createFlow(refresh, aal, returnTo);
101101
});
102102
return;
103103
}
104104

105105
// Otherwise we initialize it
106-
createFlow(refresh, aal, returnTo ?? "/");
106+
createFlow(refresh, aal, returnTo);
107107
// eslint-disable-next-line react-hooks/exhaustive-deps
108108
}, [isReady]);
109109

@@ -115,7 +115,7 @@ const KratosLogin = () => {
115115
updateLoginFlowBody: values
116116
});
117117
setLoginSuccessful(true);
118-
push(String(returnTo || "/"));
118+
push(returnTo ?? "/");
119119
} catch (error) {
120120
if ((error as AxiosError).response?.status === 400) {
121121
// Yup, it is!
@@ -157,7 +157,6 @@ const KratosLogin = () => {
157157
}
158158
}, [flow, submitFlow, credentials]);
159159

160-
161160
return (
162161
<div className="w-96">
163162
<ToasterWithCloseButton />

0 commit comments

Comments
 (0)