Skip to content

Commit

Permalink
Merge pull request #261 from supertokens/fix/axios_error_transform
Browse files Browse the repository at this point in the history
fix: axios error transformation on network errors
  • Loading branch information
porcellus authored Jun 14, 2024
2 parents 56fa018 + 09154e7 commit 9128573
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 49 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Added a warning if the SDK can't save to cookies to help people notice/debug these issues faster.

### Fixes

- Fixed an issue in the Axios interceptor that caused it to throw when encountering a network error

## [20.1.0] - 2024-05-31

### Changes
Expand Down
2 changes: 1 addition & 1 deletion bundle/bundle.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lib/build/axios.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/build/axiosError.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 21 additions & 14 deletions lib/build/axiosError.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/ts/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export function responseErrorInterceptor(axiosInstance: any) {
"responseErrorInterceptor: already intercepted: " +
(error.response && error.response.headers["x-supertokens-xhr-intercepted"])
);
if (error.response.headers["x-supertokens-xhr-intercepted"]) {
if (error.response === undefined || error.response.headers["x-supertokens-xhr-intercepted"]) {
throw error;
}
if (
Expand Down Expand Up @@ -489,6 +489,7 @@ export default class AuthHttpRequest {
logDebugMessage(
"doRequest: sessionRefreshAttempts: " + config.__supertokensSessionRefreshAttempts
);
console.log("!!!!", JSON.stringify(refreshResult));

if (refreshResult.result !== "RETRY") {
logDebugMessage("doRequest: Not retrying original request");
Expand Down
59 changes: 32 additions & 27 deletions lib/ts/axiosError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,40 +48,45 @@ function enhanceAxiosError(
return error;
}

export async function createAxiosErrorFromFetchResp(response: Response): Promise<AxiosError> {
export async function createAxiosErrorFromFetchResp(responseOrError: Response): Promise<AxiosError> {
const config = {
url: response.url,
headers: response.headers
url: responseOrError.url,
headers: responseOrError.headers
};
const contentType = response.headers.get("content-type");

let data;
if (contentType === null) {
try {
data = await response.text();
} catch {
data = "";
const isResponse = "status" in responseOrError;
let axiosResponse;
if (isResponse) {
const contentType = responseOrError.headers.get("content-type");

let data;
if (contentType === null) {
try {
data = await responseOrError.text();
} catch {
data = "";
}
} else if (contentType.includes("application/json")) {
data = await responseOrError.json();
} else if (contentType.includes("text/")) {
data = await responseOrError.text();
} else {
data = await responseOrError.blob();
}
} else if (contentType.includes("application/json")) {
data = await response.json();
} else if (contentType.includes("text/")) {
data = await response.text();
} else {
data = await response.blob();
}

const axiosResponse = {
data,
status: response.status,
statusText: response.statusText,
headers: response.headers,
config: config,
request: undefined
};
axiosResponse = {
data,
status: responseOrError.status,
statusText: responseOrError.statusText,
headers: responseOrError.headers,
config: config,
request: undefined
};
}
return enhanceAxiosError(
new Error("Request failed with status code " + response.status),
isResponse ? new Error("Request failed with status code " + responseOrError.status) : responseOrError,
config as any,
undefined,
(responseOrError as any).code,
undefined,
axiosResponse as any
);
Expand Down
90 changes: 86 additions & 4 deletions test/axios2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ describe("Axios AuthHttpRequest class tests", function () {
});

before(async function () {
spawn("./test/startServer", [
process.env.INSTALL_PATH,
process.env.NODE_PORT === undefined ? 8080 : process.env.NODE_PORT
]);
spawn(
"./test/startServer",
[process.env.INSTALL_PATH, process.env.NODE_PORT === undefined ? 8080 : process.env.NODE_PORT]
// {
// stdio: "inherit"
// }
);
await new Promise(r => setTimeout(r, 1000));
});

Expand Down Expand Up @@ -685,4 +688,83 @@ describe("Axios AuthHttpRequest class tests", function () {
await browser.close();
}
});

it("should throw error if refresh fails with a network error", async function () {
await startST(3);
const browser = await puppeteer.launch({
args: ["--no-sandbox", "--disable-setuid-sandbox"]
});

try {
const page = await browser.newPage();
await page.goto(BASE_URL + "/index.html", { waitUntil: "load" });
await page.addScriptTag({ path: `./bundle/bundle.js`, type: "text/javascript" });
// const logs = [];
// page.on("console", ev => {
// const logText = ev.text();
// // console.log(logText);
// if (logText.startsWith("com.supertokens")) {
// logs.push(logText);
// }
// });

await page.evaluate(async BASE_URL => {
const axiosInstance = axios.create({
withCredentials: true
});
const origFetch = window.fetch;
window.fetch = async (...input) => {
try {
if (input[0].endsWith("/auth/session/refresh")) {
input[0] = "http://localhost:1234/nope";
}
return await origFetch(...input);
} catch (err) {
throw err;
}
};
supertokens.init({
apiDomain: BASE_URL,
tokenTransferMethod: "cookie",
// enableDebugLogs: true,
override: {
functions: oI => ({
...oI,
addXMLHttpRequestInterceptor: () => {}
})
}
});
supertokens.addAxiosInterceptors(axiosInstance);

let userId = "testing-supertokens-react-native";

let loginResponse = await axiosInstance.post(`${BASE_URL}/login`, JSON.stringify({ userId }), {
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});
let userIdFromResponse = loginResponse.data;
assertEqual(userId, userIdFromResponse);

//check that the number of times the refreshAPI was called is 0
assert((await getNumberOfTimesRefreshCalled()) === 0);

try {
await axiosInstance("http://localhost:1234/asdf");
await axiosInstance({
url: `${BASE_URL}/`,
method: "GET",
headers: { "Cache-Control": "no-cache, private" }
});
} catch (err) {
assert(err.isAxiosError);
assert.strictEqual(err.code, "ERR_NETWORK");
assert.strictEqual(err.response, undefined);
}
}, BASE_URL);
} finally {
await browser.close();
}
});
});

0 comments on commit 9128573

Please sign in to comment.