From e7216fc9b3e014a696fd05146ddebb60db9a680a Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 12 Jul 2024 17:55:23 +0200 Subject: [PATCH 1/2] fix: remove redundant removeToken calls --- lib/build/axios.js | 325 +++++++++++++++++-------------------- lib/build/fetch.js | 158 ++++++++---------- lib/build/frontToken.js | 2 + lib/ts/antiCsrf.ts | 1 - lib/ts/axios.ts | 347 +++++++++++++++++++--------------------- lib/ts/fetch.ts | 181 ++++++++++----------- lib/ts/frontToken.ts | 2 + 7 files changed, 465 insertions(+), 551 deletions(-) diff --git a/lib/build/axios.js b/lib/build/axios.js index 98e6b27..adba576 100644 --- a/lib/build/axios.js +++ b/lib/build/axios.js @@ -135,69 +135,59 @@ export function responseInterceptor(axiosInstance) { return response => __awaiter(this, void 0, void 0, function*() { let doNotDoInterception = false; + if (!AuthHttpRequestFetch.initCalled) { + throw new Error("init function not called"); + } + logDebugMessage("responseInterceptor: started"); + logDebugMessage( + "responseInterceptor: already intercepted: " + response.headers["x-supertokens-xhr-intercepted"] + ); + let url = getUrlFromConfig(response.config); try { - if (!AuthHttpRequestFetch.initCalled) { - throw new Error("init function not called"); - } - logDebugMessage("responseInterceptor: started"); - logDebugMessage( - "responseInterceptor: already intercepted: " + response.headers["x-supertokens-xhr-intercepted"] - ); - let url = getUrlFromConfig(response.config); - try { - doNotDoInterception = - typeof url === "string" && - !AuthHttpRequestFetch.recipeImpl.shouldDoInterceptionBasedOnUrl( - url, - AuthHttpRequestFetch.config.apiDomain, - AuthHttpRequestFetch.config.sessionTokenBackendDomain - ); - } catch (err) { - // This is because when this function is called we always have a full URL (refer to getUrlFromConfig), - // so we do not need to check for the case where axios is called with just a path (for example axios.post("/login")) - throw err; - } - logDebugMessage("responseInterceptor: Value of doNotDoInterception: " + doNotDoInterception); - if (doNotDoInterception) { - logDebugMessage("responseInterceptor: Returning without interception"); - // this check means that if you are using axios via inteceptor, then we only do the refresh steps if you are calling your APIs. - return response; - } - logDebugMessage("responseInterceptor: Interception started"); - ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_RESPONSE); - const preRequestLocalSessionState = yield getLocalSessionState(); - yield saveTokensFromHeaders(response); - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers["front-token"] - ); - if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { - logDebugMessage("responseInterceptor: Status code is: " + response.status); - let config = response.config; - return AuthHttpRequest.doRequest( - config => { - // we create an instance since we don't want to intercept this. - // const instance = axios.create(); - // return instance(config); - return axiosInstance(config); - }, - config, + doNotDoInterception = + typeof url === "string" && + !AuthHttpRequestFetch.recipeImpl.shouldDoInterceptionBasedOnUrl( url, - response, - true - ); - } else { - return response; - } - } finally { - if (!doNotDoInterception && (yield getLocalSessionState()).status !== "EXISTS") { - logDebugMessage( - "responseInterceptor: local session doesn't exist, so removing anti-csrf and sFrontToken" + AuthHttpRequestFetch.config.apiDomain, + AuthHttpRequestFetch.config.sessionTokenBackendDomain ); - yield AntiCSRF.removeToken(); - yield FrontToken.removeToken(); - } + } catch (err) { + // This is because when this function is called we always have a full URL (refer to getUrlFromConfig), + // so we do not need to check for the case where axios is called with just a path (for example axios.post("/login")) + throw err; + } + logDebugMessage("responseInterceptor: Value of doNotDoInterception: " + doNotDoInterception); + if (doNotDoInterception) { + logDebugMessage("responseInterceptor: Returning without interception"); + // this check means that if you are using axios via inteceptor, then we only do the refresh steps if you are calling your APIs. + return response; + } + logDebugMessage("responseInterceptor: Interception started"); + ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_RESPONSE); + const preRequestLocalSessionState = yield getLocalSessionState(); + yield saveTokensFromHeaders(response); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers["front-token"] + ); + if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { + logDebugMessage("responseInterceptor: Status code is: " + response.status); + let config = response.config; + return AuthHttpRequest.doRequest( + config => { + // we create an instance since we don't want to intercept this. + // const instance = axios.create(); + // return instance(config); + return axiosInstance(config); + }, + config, + url, + response, + true + ); + } else { + return response; } }); } @@ -276,140 +266,123 @@ AuthHttpRequest.doRequest = (httpCall, config, url, prevResponse, prevError, via } logDebugMessage("doRequest: Interception started"); config = yield removeAuthHeaderIfMatchesLocalToken(config); - try { - let returnObj = undefined; - while (true) { - // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. - // to avoid race conditions - const preRequestLocalSessionState = yield getLocalSessionState(); - let configWithAntiCsrf = config; - if (preRequestLocalSessionState.status === "EXISTS") { - const antiCsrfToken = yield AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); - if (antiCsrfToken !== undefined) { - logDebugMessage("doRequest: Adding anti-csrf token to request"); - configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { - headers: - configWithAntiCsrf === undefined - ? { - "anti-csrf": antiCsrfToken - } - : Object.assign(Object.assign({}, configWithAntiCsrf.headers), { - "anti-csrf": antiCsrfToken - }) - }); - } - } - if ( - AuthHttpRequestFetch.config.autoAddCredentials && - configWithAntiCsrf.withCredentials === undefined - ) { - logDebugMessage("doRequest: Adding credentials include"); + let returnObj = undefined; + while (true) { + // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. + // to avoid race conditions + const preRequestLocalSessionState = yield getLocalSessionState(); + let configWithAntiCsrf = config; + if (preRequestLocalSessionState.status === "EXISTS") { + const antiCsrfToken = yield AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); + if (antiCsrfToken !== undefined) { + logDebugMessage("doRequest: Adding anti-csrf token to request"); configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { - withCredentials: true + headers: + configWithAntiCsrf === undefined + ? { + "anti-csrf": antiCsrfToken + } + : Object.assign(Object.assign({}, configWithAntiCsrf.headers), { + "anti-csrf": antiCsrfToken + }) }); } - // adding rid for anti-csrf protection: Anti-csrf via custom header - logDebugMessage("doRequest: Adding rid header: anti-csrf (May get overriden by user's rid)"); - configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { - headers: - configWithAntiCsrf === undefined - ? { - rid: "anti-csrf" - } - : Object.assign({ rid: "anti-csrf" }, configWithAntiCsrf.headers) - }); - const transferMethod = AuthHttpRequestFetch.config.tokenTransferMethod; - logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); - configWithAntiCsrf.headers["st-auth-mode"] = transferMethod; - yield setAuthorizationHeaderIfRequired(configWithAntiCsrf); - try { - let localPrevError = prevError; - let localPrevResponse = prevResponse; - prevError = undefined; - prevResponse = undefined; - if (localPrevError !== undefined) { - logDebugMessage("doRequest: Not making call because localPrevError is not undefined"); - throw localPrevError; - } - if (localPrevResponse !== undefined) { - logDebugMessage("doRequest: Not making call because localPrevResponse is not undefined"); - } else { - logDebugMessage("doRequest: Making user's http call"); - } - let response = - localPrevResponse === undefined ? yield httpCall(configWithAntiCsrf) : localPrevResponse; - // NOTE: No need to check for unauthorized response status here for session refresh, - // as we only reach this point on a successful response. Axios handles error responses - // by throwing an error, which is handled in the catch block. - logDebugMessage("doRequest: User's http call ended"); + } + if (AuthHttpRequestFetch.config.autoAddCredentials && configWithAntiCsrf.withCredentials === undefined) { + logDebugMessage("doRequest: Adding credentials include"); + configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { withCredentials: true }); + } + // adding rid for anti-csrf protection: Anti-csrf via custom header + logDebugMessage("doRequest: Adding rid header: anti-csrf (May get overriden by user's rid)"); + configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { + headers: + configWithAntiCsrf === undefined + ? { + rid: "anti-csrf" + } + : Object.assign({ rid: "anti-csrf" }, configWithAntiCsrf.headers) + }); + const transferMethod = AuthHttpRequestFetch.config.tokenTransferMethod; + logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); + configWithAntiCsrf.headers["st-auth-mode"] = transferMethod; + yield setAuthorizationHeaderIfRequired(configWithAntiCsrf); + try { + let localPrevError = prevError; + let localPrevResponse = prevResponse; + prevError = undefined; + prevResponse = undefined; + if (localPrevError !== undefined) { + logDebugMessage("doRequest: Not making call because localPrevError is not undefined"); + throw localPrevError; + } + if (localPrevResponse !== undefined) { + logDebugMessage("doRequest: Not making call because localPrevResponse is not undefined"); + } else { + logDebugMessage("doRequest: Making user's http call"); + } + let response = localPrevResponse === undefined ? yield httpCall(configWithAntiCsrf) : localPrevResponse; + // NOTE: No need to check for unauthorized response status here for session refresh, + // as we only reach this point on a successful response. Axios handles error responses + // by throwing an error, which is handled in the catch block. + logDebugMessage("doRequest: User's http call ended"); + yield saveTokensFromHeaders(response); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers["front-token"] + ); + return response; + } catch (err) { + const response = err.response; + if (response !== undefined) { yield saveTokensFromHeaders(response); fireSessionUpdateEventsIfNecessary( preRequestLocalSessionState.status === "EXISTS", response.status, response.headers["front-token"] ); - return response; - } catch (err) { - const response = err.response; - if (response !== undefined) { - yield saveTokensFromHeaders(response); - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers["front-token"] - ); - if (err.response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { - logDebugMessage("doRequest: Status code is: " + response.status); - /** - * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. - * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. - * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. - */ - if (hasExceededMaxSessionRefreshAttempts(config)) { - logDebugMessage( - `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${config.__supertokensSessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh}` - ); - throw new Error( - `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` - ); - } - const refreshResult = yield onUnauthorisedResponse(preRequestLocalSessionState); - incrementSessionRefreshAttemptCount(config); + if (err.response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { + logDebugMessage("doRequest: Status code is: " + response.status); + /** + * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. + * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. + * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. + */ + if (hasExceededMaxSessionRefreshAttempts(config)) { logDebugMessage( - "doRequest: sessionRefreshAttempts: " + config.__supertokensSessionRefreshAttempts + `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${config.__supertokensSessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh}` + ); + throw new Error( + `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` ); - if (refreshResult.result !== "RETRY") { - logDebugMessage("doRequest: Not retrying original request"); - // Returning refreshResult.error as an Axios Error if we attempted a refresh - // Returning the original error if we did not attempt refreshing - returnObj = - refreshResult.error !== undefined - ? yield createAxiosErrorFromFetchResp(refreshResult.error) - : err; - break; - } - logDebugMessage("doRequest: Retrying original request"); - } else { - throw err; } + const refreshResult = yield onUnauthorisedResponse(preRequestLocalSessionState); + incrementSessionRefreshAttemptCount(config); + logDebugMessage( + "doRequest: sessionRefreshAttempts: " + config.__supertokensSessionRefreshAttempts + ); + if (refreshResult.result !== "RETRY") { + logDebugMessage("doRequest: Not retrying original request"); + // Returning refreshResult.error as an Axios Error if we attempted a refresh + // Returning the original error if we did not attempt refreshing + returnObj = + refreshResult.error !== undefined + ? yield createAxiosErrorFromFetchResp(refreshResult.error) + : err; + break; + } + logDebugMessage("doRequest: Retrying original request"); } else { throw err; } + } else { + throw err; } } - // if it comes here, means we called break. which happens only if we have logged out. - // which means it's a 401, so we throw - throw returnObj; - } finally { - // If we get here we already tried refreshing so we should have the already id refresh token either in EXISTS or NOT_EXISTS, so no need to call the backend - // The backend should not be down if we get here, but even if it were we shouldn't need to call refresh - const postRequestLocalSessionState = yield getLocalSessionState(); - if (postRequestLocalSessionState.status === "NOT_EXISTS") { - logDebugMessage("doRequest: local session doesn't exist, so removing anti-csrf and sFrontToken"); - yield AntiCSRF.removeToken(); - yield FrontToken.removeToken(); - } } + // if it comes here, means we called break. which happens only if we have logged out. + // which means it's a 401, so we throw + throw returnObj; }); function saveTokensFromHeaders(response) { return __awaiter(this, void 0, void 0, function*() { diff --git a/lib/build/fetch.js b/lib/build/fetch.js index 8130aa1..58efec2 100644 --- a/lib/build/fetch.js +++ b/lib/build/fetch.js @@ -163,93 +163,82 @@ AuthHttpRequest.doRequest = (httpCall, config, url) => } logDebugMessage("doRequest: Interception started"); ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_REQUEST); - try { - let sessionRefreshAttempts = 0; - let returnObj = undefined; - while (true) { - // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. - // to avoid race conditions - const preRequestLocalSessionState = yield getLocalSessionState(); - const clonedHeaders = new Headers(originalHeaders); - let configWithAntiCsrf = Object.assign(Object.assign({}, config), { headers: clonedHeaders }); - if (preRequestLocalSessionState.status === "EXISTS") { - const antiCsrfToken = yield AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); - if (antiCsrfToken !== undefined) { - logDebugMessage("doRequest: Adding anti-csrf token to request"); - clonedHeaders.set("anti-csrf", antiCsrfToken); - } + let sessionRefreshAttempts = 0; + let returnObj = undefined; + while (true) { + // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. + // to avoid race conditions + const preRequestLocalSessionState = yield getLocalSessionState(); + const clonedHeaders = new Headers(originalHeaders); + let configWithAntiCsrf = Object.assign(Object.assign({}, config), { headers: clonedHeaders }); + if (preRequestLocalSessionState.status === "EXISTS") { + const antiCsrfToken = yield AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); + if (antiCsrfToken !== undefined) { + logDebugMessage("doRequest: Adding anti-csrf token to request"); + clonedHeaders.set("anti-csrf", antiCsrfToken); } - if (AuthHttpRequest.config.autoAddCredentials) { - logDebugMessage("doRequest: Adding credentials include"); - if (configWithAntiCsrf === undefined) { - configWithAntiCsrf = { - credentials: "include" - }; - } else if (configWithAntiCsrf.credentials === undefined) { - configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { - credentials: "include" - }); - } + } + if (AuthHttpRequest.config.autoAddCredentials) { + logDebugMessage("doRequest: Adding credentials include"); + if (configWithAntiCsrf === undefined) { + configWithAntiCsrf = { + credentials: "include" + }; + } else if (configWithAntiCsrf.credentials === undefined) { + configWithAntiCsrf = Object.assign(Object.assign({}, configWithAntiCsrf), { + credentials: "include" + }); } - // adding rid for anti-csrf protection: Anti-csrf via custom header - if (!clonedHeaders.has("rid")) { - logDebugMessage("doRequest: Adding rid header: anti-csrf"); - clonedHeaders.set("rid", "anti-csrf"); - } else { - logDebugMessage("doRequest: rid header was already there in request"); + } + // adding rid for anti-csrf protection: Anti-csrf via custom header + if (!clonedHeaders.has("rid")) { + logDebugMessage("doRequest: Adding rid header: anti-csrf"); + clonedHeaders.set("rid", "anti-csrf"); + } else { + logDebugMessage("doRequest: rid header was already there in request"); + } + const transferMethod = AuthHttpRequest.config.tokenTransferMethod; + logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); + clonedHeaders.set("st-auth-mode", transferMethod); + yield setAuthorizationHeaderIfRequired(clonedHeaders); + logDebugMessage("doRequest: Making user's http call"); + let response = yield httpCall(configWithAntiCsrf); + logDebugMessage("doRequest: User's http call ended"); + yield saveTokensFromHeaders(response); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers.get("front-token") + ); + if (response.status === AuthHttpRequest.config.sessionExpiredStatusCode) { + logDebugMessage("doRequest: Status code is: " + response.status); + /** + * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. + * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. + * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. + */ + if (sessionRefreshAttempts >= AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh) { + logDebugMessage( + `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${sessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh}` + ); + throw new Error( + `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` + ); } - const transferMethod = AuthHttpRequest.config.tokenTransferMethod; - logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); - clonedHeaders.set("st-auth-mode", transferMethod); - yield setAuthorizationHeaderIfRequired(clonedHeaders); - logDebugMessage("doRequest: Making user's http call"); - let response = yield httpCall(configWithAntiCsrf); - logDebugMessage("doRequest: User's http call ended"); - yield saveTokensFromHeaders(response); - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers.get("front-token") - ); - if (response.status === AuthHttpRequest.config.sessionExpiredStatusCode) { - logDebugMessage("doRequest: Status code is: " + response.status); - /** - * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. - * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. - * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. - */ - if (sessionRefreshAttempts >= AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh) { - logDebugMessage( - `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${sessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh}` - ); - throw new Error( - `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` - ); - } - let refreshResponse = yield onUnauthorisedResponse(preRequestLocalSessionState); - sessionRefreshAttempts++; - logDebugMessage("doRequest: sessionRefreshAttempts: " + sessionRefreshAttempts); - if (refreshResponse.result !== "RETRY") { - logDebugMessage("doRequest: Not retrying original request"); - returnObj = refreshResponse.error !== undefined ? refreshResponse.error : response; - break; - } - } else { - return response; + let refreshResponse = yield onUnauthorisedResponse(preRequestLocalSessionState); + sessionRefreshAttempts++; + logDebugMessage("doRequest: sessionRefreshAttempts: " + sessionRefreshAttempts); + if (refreshResponse.result !== "RETRY") { + logDebugMessage("doRequest: Not retrying original request"); + returnObj = refreshResponse.error !== undefined ? refreshResponse.error : response; + break; } - } - // if it comes here, means we breaked. which happens only if we have logged out. - return returnObj; - } finally { - // If we get here we already tried refreshing so we should have the already id refresh token either in EXISTS or NOT_EXISTS, so no need to call the backend - // or the backend is down and we don't need to call it. - const postRequestLocalSessionState = yield getLocalSessionState(); - if (postRequestLocalSessionState.status === "NOT_EXISTS") { - logDebugMessage("doRequest: local session doesn't exist, so removing anti-csrf and sFrontToken"); - yield AntiCSRF.removeToken(); - yield FrontToken.removeToken(); + } else { + return response; } } + // if it comes here, means we breaked. which happens only if we have logged out. + return returnObj; }); AuthHttpRequest.attemptRefreshingSession = () => __awaiter(void 0, void 0, void 0, function*() { @@ -374,13 +363,6 @@ export function onUnauthorisedResponse(preRequestLocalSessionState) { } finally { lock.unlock(LOCK_NAME); logDebugMessage("onUnauthorisedResponse: Released lock"); - if ((yield getLocalSessionState()).status === "NOT_EXISTS") { - logDebugMessage( - "onUnauthorisedResponse: local session doesn't exist, so removing anti-csrf and sFrontToken" - ); - yield AntiCSRF.removeToken(); - yield FrontToken.removeToken(); - } } }); } diff --git a/lib/build/frontToken.js b/lib/build/frontToken.js index 9c53dde..88886f5 100644 --- a/lib/build/frontToken.js +++ b/lib/build/frontToken.js @@ -33,6 +33,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { decode as atob } from "base-64"; import { getLocalSessionState, saveLastAccessTokenUpdate, setToken } from "./utils"; import { logDebugMessage } from "./logger"; +import AntiCSRF from "./antiCsrf"; const FRONT_TOKEN_KEY = "supertokens-rn-front-token-key"; const FRONT_TOKEN_NAME = "sFrontToken"; export default class FrontToken { @@ -126,6 +127,7 @@ export default class FrontToken { yield this.setFrontToken(undefined); yield setToken("access", ""); yield setToken("refresh", ""); + yield AntiCSRF.removeToken(); FrontToken.waiters.forEach(f => f(undefined)); FrontToken.waiters = []; }); diff --git a/lib/ts/antiCsrf.ts b/lib/ts/antiCsrf.ts index 6fa5efb..b0ecd49 100644 --- a/lib/ts/antiCsrf.ts +++ b/lib/ts/antiCsrf.ts @@ -14,7 +14,6 @@ */ import AsyncStorage from "@react-native-async-storage/async-storage"; -import AuthHttpRequest from "./fetch"; import { getLocalSessionState } from "./utils"; import { logDebugMessage } from "./logger"; diff --git a/lib/ts/axios.ts b/lib/ts/axios.ts index b943b69..ecff3e5 100644 --- a/lib/ts/axios.ts +++ b/lib/ts/axios.ts @@ -150,75 +150,65 @@ export function responseInterceptor(axiosInstance: any) { return async (response: AxiosResponse) => { let doNotDoInterception = false; + if (!AuthHttpRequestFetch.initCalled) { + throw new Error("init function not called"); + } + logDebugMessage("responseInterceptor: started"); + logDebugMessage( + "responseInterceptor: already intercepted: " + response.headers["x-supertokens-xhr-intercepted"] + ); + let url = getUrlFromConfig(response.config); + try { - if (!AuthHttpRequestFetch.initCalled) { - throw new Error("init function not called"); - } - logDebugMessage("responseInterceptor: started"); - logDebugMessage( - "responseInterceptor: already intercepted: " + response.headers["x-supertokens-xhr-intercepted"] - ); - let url = getUrlFromConfig(response.config); + doNotDoInterception = + typeof url === "string" && + !AuthHttpRequestFetch.recipeImpl.shouldDoInterceptionBasedOnUrl( + url, + AuthHttpRequestFetch.config.apiDomain, + AuthHttpRequestFetch.config.sessionTokenBackendDomain + ); + } catch (err) { + // This is because when this function is called we always have a full URL (refer to getUrlFromConfig), + // so we do not need to check for the case where axios is called with just a path (for example axios.post("/login")) + throw err; + } - try { - doNotDoInterception = - typeof url === "string" && - !AuthHttpRequestFetch.recipeImpl.shouldDoInterceptionBasedOnUrl( - url, - AuthHttpRequestFetch.config.apiDomain, - AuthHttpRequestFetch.config.sessionTokenBackendDomain - ); - } catch (err) { - // This is because when this function is called we always have a full URL (refer to getUrlFromConfig), - // so we do not need to check for the case where axios is called with just a path (for example axios.post("/login")) - throw err; - } + logDebugMessage("responseInterceptor: Value of doNotDoInterception: " + doNotDoInterception); + if (doNotDoInterception) { + logDebugMessage("responseInterceptor: Returning without interception"); + // this check means that if you are using axios via inteceptor, then we only do the refresh steps if you are calling your APIs. + return response; + } + logDebugMessage("responseInterceptor: Interception started"); - logDebugMessage("responseInterceptor: Value of doNotDoInterception: " + doNotDoInterception); - if (doNotDoInterception) { - logDebugMessage("responseInterceptor: Returning without interception"); - // this check means that if you are using axios via inteceptor, then we only do the refresh steps if you are calling your APIs. - return response; - } - logDebugMessage("responseInterceptor: Interception started"); + ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_RESPONSE); - ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_RESPONSE); + const preRequestLocalSessionState = await getLocalSessionState(); + await saveTokensFromHeaders(response); - const preRequestLocalSessionState = await getLocalSessionState(); - await saveTokensFromHeaders(response); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers["front-token"] + ); - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers["front-token"] + if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { + logDebugMessage("responseInterceptor: Status code is: " + response.status); + let config = response.config; + return AuthHttpRequest.doRequest( + (config: AxiosRequestConfig) => { + // we create an instance since we don't want to intercept this. + // const instance = axios.create(); + // return instance(config); + return axiosInstance(config); + }, + config, + url, + response, + true ); - - if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { - logDebugMessage("responseInterceptor: Status code is: " + response.status); - let config = response.config; - return AuthHttpRequest.doRequest( - (config: AxiosRequestConfig) => { - // we create an instance since we don't want to intercept this. - // const instance = axios.create(); - // return instance(config); - return axiosInstance(config); - }, - config, - url, - response, - true - ); - } else { - return response; - } - } finally { - if (!doNotDoInterception && (await getLocalSessionState()).status !== "EXISTS") { - logDebugMessage( - "responseInterceptor: local session doesn't exist, so removing anti-csrf and sFrontToken" - ); - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); - } + } else { + return response; } }; } @@ -308,88 +298,95 @@ export default class AuthHttpRequest { logDebugMessage("doRequest: Interception started"); config = await removeAuthHeaderIfMatchesLocalToken(config); - try { - let returnObj = undefined; - while (true) { - // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. - // to avoid race conditions - const preRequestLocalSessionState = await getLocalSessionState(); - let configWithAntiCsrf: AxiosRequestConfig = config; - - if (preRequestLocalSessionState.status === "EXISTS") { - const antiCsrfToken = await AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); - if (antiCsrfToken !== undefined) { - logDebugMessage("doRequest: Adding anti-csrf token to request"); - configWithAntiCsrf = { - ...configWithAntiCsrf, - headers: - configWithAntiCsrf === undefined - ? { - "anti-csrf": antiCsrfToken - } - : { - ...configWithAntiCsrf.headers, - "anti-csrf": antiCsrfToken - } - }; - } - } + let returnObj = undefined; + while (true) { + // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. + // to avoid race conditions + const preRequestLocalSessionState = await getLocalSessionState(); + let configWithAntiCsrf: AxiosRequestConfig = config; - if ( - AuthHttpRequestFetch.config.autoAddCredentials && - configWithAntiCsrf.withCredentials === undefined - ) { - logDebugMessage("doRequest: Adding credentials include"); + if (preRequestLocalSessionState.status === "EXISTS") { + const antiCsrfToken = await AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); + if (antiCsrfToken !== undefined) { + logDebugMessage("doRequest: Adding anti-csrf token to request"); configWithAntiCsrf = { ...configWithAntiCsrf, - withCredentials: true + headers: + configWithAntiCsrf === undefined + ? { + "anti-csrf": antiCsrfToken + } + : { + ...configWithAntiCsrf.headers, + "anti-csrf": antiCsrfToken + } }; } + } - // adding rid for anti-csrf protection: Anti-csrf via custom header - logDebugMessage("doRequest: Adding rid header: anti-csrf (May get overriden by user's rid)"); + if (AuthHttpRequestFetch.config.autoAddCredentials && configWithAntiCsrf.withCredentials === undefined) { + logDebugMessage("doRequest: Adding credentials include"); configWithAntiCsrf = { ...configWithAntiCsrf, - headers: - configWithAntiCsrf === undefined - ? { - rid: "anti-csrf" - } - : { - rid: "anti-csrf", - ...configWithAntiCsrf.headers - } + withCredentials: true }; + } + + // adding rid for anti-csrf protection: Anti-csrf via custom header + logDebugMessage("doRequest: Adding rid header: anti-csrf (May get overriden by user's rid)"); + configWithAntiCsrf = { + ...configWithAntiCsrf, + headers: + configWithAntiCsrf === undefined + ? { + rid: "anti-csrf" + } + : { + rid: "anti-csrf", + ...configWithAntiCsrf.headers + } + }; - const transferMethod = AuthHttpRequestFetch.config.tokenTransferMethod; - logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); - configWithAntiCsrf.headers!["st-auth-mode"] = transferMethod; + const transferMethod = AuthHttpRequestFetch.config.tokenTransferMethod; + logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); + configWithAntiCsrf.headers!["st-auth-mode"] = transferMethod; - await setAuthorizationHeaderIfRequired(configWithAntiCsrf); + await setAuthorizationHeaderIfRequired(configWithAntiCsrf); - try { - let localPrevError = prevError; - let localPrevResponse = prevResponse; - prevError = undefined; - prevResponse = undefined; - if (localPrevError !== undefined) { - logDebugMessage("doRequest: Not making call because localPrevError is not undefined"); - throw localPrevError; - } - if (localPrevResponse !== undefined) { - logDebugMessage("doRequest: Not making call because localPrevResponse is not undefined"); - } else { - logDebugMessage("doRequest: Making user's http call"); - } - let response = - localPrevResponse === undefined ? await httpCall(configWithAntiCsrf) : localPrevResponse; + try { + let localPrevError = prevError; + let localPrevResponse = prevResponse; + prevError = undefined; + prevResponse = undefined; + if (localPrevError !== undefined) { + logDebugMessage("doRequest: Not making call because localPrevError is not undefined"); + throw localPrevError; + } + if (localPrevResponse !== undefined) { + logDebugMessage("doRequest: Not making call because localPrevResponse is not undefined"); + } else { + logDebugMessage("doRequest: Making user's http call"); + } + let response = localPrevResponse === undefined ? await httpCall(configWithAntiCsrf) : localPrevResponse; + + // NOTE: No need to check for unauthorized response status here for session refresh, + // as we only reach this point on a successful response. Axios handles error responses + // by throwing an error, which is handled in the catch block. - // NOTE: No need to check for unauthorized response status here for session refresh, - // as we only reach this point on a successful response. Axios handles error responses - // by throwing an error, which is handled in the catch block. + logDebugMessage("doRequest: User's http call ended"); - logDebugMessage("doRequest: User's http call ended"); + await saveTokensFromHeaders(response); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers["front-token"] + ); + + return response; + } catch (err) { + const response = (err as any).response; + if (response !== undefined) { await saveTokensFromHeaders(response); fireSessionUpdateEventsIfNecessary( @@ -398,73 +395,51 @@ export default class AuthHttpRequest { response.headers["front-token"] ); - return response; - } catch (err) { - const response = (err as any).response; - if (response !== undefined) { - await saveTokensFromHeaders(response); - - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers["front-token"] - ); + if (err.response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { + logDebugMessage("doRequest: Status code is: " + response.status); - if (err.response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) { - logDebugMessage("doRequest: Status code is: " + response.status); - - /** - * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. - * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. - * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. - */ - if (hasExceededMaxSessionRefreshAttempts(config)) { - logDebugMessage( - `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${config.__supertokensSessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh}` - ); - throw new Error( - `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` - ); - } - - const refreshResult = await onUnauthorisedResponse(preRequestLocalSessionState); - incrementSessionRefreshAttemptCount(config); + /** + * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. + * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. + * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. + */ + if (hasExceededMaxSessionRefreshAttempts(config)) { logDebugMessage( - "doRequest: sessionRefreshAttempts: " + config.__supertokensSessionRefreshAttempts + `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${config.__supertokensSessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh}` ); - if (refreshResult.result !== "RETRY") { - logDebugMessage("doRequest: Not retrying original request"); - // Returning refreshResult.error as an Axios Error if we attempted a refresh - // Returning the original error if we did not attempt refreshing - returnObj = - refreshResult.error !== undefined - ? await createAxiosErrorFromFetchResp(refreshResult.error) - : err; - break; - } - logDebugMessage("doRequest: Retrying original request"); - } else { - throw err; + throw new Error( + `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequestFetch.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` + ); + } + + const refreshResult = await onUnauthorisedResponse(preRequestLocalSessionState); + incrementSessionRefreshAttemptCount(config); + logDebugMessage( + "doRequest: sessionRefreshAttempts: " + config.__supertokensSessionRefreshAttempts + ); + if (refreshResult.result !== "RETRY") { + logDebugMessage("doRequest: Not retrying original request"); + // Returning refreshResult.error as an Axios Error if we attempted a refresh + // Returning the original error if we did not attempt refreshing + returnObj = + refreshResult.error !== undefined + ? await createAxiosErrorFromFetchResp(refreshResult.error) + : err; + break; } + logDebugMessage("doRequest: Retrying original request"); } else { throw err; } + } else { + throw err; } } - - // if it comes here, means we called break. which happens only if we have logged out. - // which means it's a 401, so we throw - throw returnObj; - } finally { - // If we get here we already tried refreshing so we should have the already id refresh token either in EXISTS or NOT_EXISTS, so no need to call the backend - // The backend should not be down if we get here, but even if it were we shouldn't need to call refresh - const postRequestLocalSessionState = await getLocalSessionState(); - if (postRequestLocalSessionState.status === "NOT_EXISTS") { - logDebugMessage("doRequest: local session doesn't exist, so removing anti-csrf and sFrontToken"); - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); - } } + + // if it comes here, means we called break. which happens only if we have logged out. + // which means it's a 401, so we throw + throw returnObj; }; } diff --git a/lib/ts/fetch.ts b/lib/ts/fetch.ts index fb67070..bf2d8d1 100644 --- a/lib/ts/fetch.ts +++ b/lib/ts/fetch.ts @@ -162,112 +162,101 @@ export default class AuthHttpRequest { logDebugMessage("doRequest: Interception started"); ProcessState.getInstance().addState(PROCESS_STATE.CALLING_INTERCEPTION_REQUEST); - try { - let sessionRefreshAttempts = 0; - let returnObj = undefined; - while (true) { - // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. - // to avoid race conditions - const preRequestLocalSessionState = await getLocalSessionState(); - const clonedHeaders = new Headers(originalHeaders); - - let configWithAntiCsrf: RequestInit | undefined = { - ...config, - headers: clonedHeaders - }; - - if (preRequestLocalSessionState.status === "EXISTS") { - const antiCsrfToken = await AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); - if (antiCsrfToken !== undefined) { - logDebugMessage("doRequest: Adding anti-csrf token to request"); - clonedHeaders.set("anti-csrf", antiCsrfToken); - } + let sessionRefreshAttempts = 0; + let returnObj = undefined; + while (true) { + // we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. + // to avoid race conditions + const preRequestLocalSessionState = await getLocalSessionState(); + const clonedHeaders = new Headers(originalHeaders); + + let configWithAntiCsrf: RequestInit | undefined = { + ...config, + headers: clonedHeaders + }; + + if (preRequestLocalSessionState.status === "EXISTS") { + const antiCsrfToken = await AntiCSRF.getToken(preRequestLocalSessionState.lastAccessTokenUpdate); + if (antiCsrfToken !== undefined) { + logDebugMessage("doRequest: Adding anti-csrf token to request"); + clonedHeaders.set("anti-csrf", antiCsrfToken); } + } - if (AuthHttpRequest.config.autoAddCredentials) { - logDebugMessage("doRequest: Adding credentials include"); - if (configWithAntiCsrf === undefined) { - configWithAntiCsrf = { - credentials: "include" - }; - } else if (configWithAntiCsrf.credentials === undefined) { - configWithAntiCsrf = { - ...configWithAntiCsrf, - credentials: "include" - }; - } + if (AuthHttpRequest.config.autoAddCredentials) { + logDebugMessage("doRequest: Adding credentials include"); + if (configWithAntiCsrf === undefined) { + configWithAntiCsrf = { + credentials: "include" + }; + } else if (configWithAntiCsrf.credentials === undefined) { + configWithAntiCsrf = { + ...configWithAntiCsrf, + credentials: "include" + }; } + } - // adding rid for anti-csrf protection: Anti-csrf via custom header - if (!clonedHeaders.has("rid")) { - logDebugMessage("doRequest: Adding rid header: anti-csrf"); - clonedHeaders.set("rid", "anti-csrf"); - } else { - logDebugMessage("doRequest: rid header was already there in request"); - } + // adding rid for anti-csrf protection: Anti-csrf via custom header + if (!clonedHeaders.has("rid")) { + logDebugMessage("doRequest: Adding rid header: anti-csrf"); + clonedHeaders.set("rid", "anti-csrf"); + } else { + logDebugMessage("doRequest: rid header was already there in request"); + } - const transferMethod = AuthHttpRequest.config.tokenTransferMethod; - logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); - clonedHeaders.set("st-auth-mode", transferMethod); + const transferMethod = AuthHttpRequest.config.tokenTransferMethod; + logDebugMessage("doRequest: Adding st-auth-mode header: " + transferMethod); + clonedHeaders.set("st-auth-mode", transferMethod); - await setAuthorizationHeaderIfRequired(clonedHeaders); + await setAuthorizationHeaderIfRequired(clonedHeaders); - logDebugMessage("doRequest: Making user's http call"); - let response = await httpCall(configWithAntiCsrf); - logDebugMessage("doRequest: User's http call ended"); + logDebugMessage("doRequest: Making user's http call"); + let response = await httpCall(configWithAntiCsrf); + logDebugMessage("doRequest: User's http call ended"); - await saveTokensFromHeaders(response); + await saveTokensFromHeaders(response); - fireSessionUpdateEventsIfNecessary( - preRequestLocalSessionState.status === "EXISTS", - response.status, - response.headers.get("front-token") - ); + fireSessionUpdateEventsIfNecessary( + preRequestLocalSessionState.status === "EXISTS", + response.status, + response.headers.get("front-token") + ); - if (response.status === AuthHttpRequest.config.sessionExpiredStatusCode) { - logDebugMessage("doRequest: Status code is: " + response.status); - - /** - * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. - * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. - * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. - */ - if (sessionRefreshAttempts >= AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh) { - logDebugMessage( - `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${sessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh}` - ); - throw new Error( - `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` - ); - } - - let refreshResponse = await onUnauthorisedResponse(preRequestLocalSessionState); - - sessionRefreshAttempts++; - logDebugMessage("doRequest: sessionRefreshAttempts: " + sessionRefreshAttempts); - - if (refreshResponse.result !== "RETRY") { - logDebugMessage("doRequest: Not retrying original request"); - returnObj = refreshResponse.error !== undefined ? refreshResponse.error : response; - break; - } - } else { - return response; + if (response.status === AuthHttpRequest.config.sessionExpiredStatusCode) { + logDebugMessage("doRequest: Status code is: " + response.status); + + /** + * An API may return a 401 error response even with a valid session, causing a session refresh loop in the interceptor. + * To prevent this infinite loop, we break out of the loop after retrying the original request a specified number of times. + * The maximum number of retry attempts is defined by maxRetryAttemptsForSessionRefresh config variable. + */ + if (sessionRefreshAttempts >= AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh) { + logDebugMessage( + `doRequest: Maximum session refresh attempts reached. sessionRefreshAttempts: ${sessionRefreshAttempts}, maxRetryAttemptsForSessionRefresh: ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh}` + ); + throw new Error( + `Received a 401 response from ${url}. Attempted to refresh the session and retry the request with the updated session tokens ${AuthHttpRequest.config.maxRetryAttemptsForSessionRefresh} times, but each attempt resulted in a 401 error. The maximum session refresh limit has been reached. Please investigate your API. To increase the session refresh attempts, update maxRetryAttemptsForSessionRefresh in the config.` + ); } - } - // if it comes here, means we breaked. which happens only if we have logged out. - return returnObj; - } finally { - // If we get here we already tried refreshing so we should have the already id refresh token either in EXISTS or NOT_EXISTS, so no need to call the backend - // or the backend is down and we don't need to call it. - const postRequestLocalSessionState = await getLocalSessionState(); - if (postRequestLocalSessionState.status === "NOT_EXISTS") { - logDebugMessage("doRequest: local session doesn't exist, so removing anti-csrf and sFrontToken"); - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); + let refreshResponse = await onUnauthorisedResponse(preRequestLocalSessionState); + + sessionRefreshAttempts++; + logDebugMessage("doRequest: sessionRefreshAttempts: " + sessionRefreshAttempts); + + if (refreshResponse.result !== "RETRY") { + logDebugMessage("doRequest: Not retrying original request"); + returnObj = refreshResponse.error !== undefined ? refreshResponse.error : response; + break; + } + } else { + return response; } } + + // if it comes here, means we breaked. which happens only if we have logged out. + return returnObj; }; static attemptRefreshingSession = async (): Promise => { @@ -416,14 +405,6 @@ export async function onUnauthorisedResponse( } finally { lock.unlock(LOCK_NAME); logDebugMessage("onUnauthorisedResponse: Released lock"); - - if ((await getLocalSessionState()).status === "NOT_EXISTS") { - logDebugMessage( - "onUnauthorisedResponse: local session doesn't exist, so removing anti-csrf and sFrontToken" - ); - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); - } } } diff --git a/lib/ts/frontToken.ts b/lib/ts/frontToken.ts index 0de1d72..28a287b 100644 --- a/lib/ts/frontToken.ts +++ b/lib/ts/frontToken.ts @@ -3,6 +3,7 @@ import AuthHttpRequest from "./fetch"; import { decode as atob } from "base-64"; import { getLocalSessionState, saveLastAccessTokenUpdate, setToken } from "./utils"; import { logDebugMessage } from "./logger"; +import AntiCSRF from "./antiCsrf"; const FRONT_TOKEN_KEY = "supertokens-rn-front-token-key"; const FRONT_TOKEN_NAME = "sFrontToken"; @@ -113,6 +114,7 @@ export default class FrontToken { await this.setFrontToken(undefined); await setToken("access", ""); await setToken("refresh", ""); + await AntiCSRF.removeToken(); FrontToken.waiters.forEach(f => f(undefined)); FrontToken.waiters = []; } From a01ca49386a8c17854be2226700cffc8ea8b4f5b Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 12 Jul 2024 18:29:51 +0200 Subject: [PATCH 2/2] chore: bump version --- CHANGELOG.md | 6 ++++++ lib/build/version.d.ts | 2 +- lib/build/version.js | 2 +- lib/ts/version.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e6ada..6e45f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [5.1.2] - 2024-07-12 + +### Changes + +- Removed redundant calls to `removeToken` + ## [5.1.1] - 2024-06-12 ### Fixes diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index 8c57f62..9952362 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1,2 +1,2 @@ -export declare const package_version = "5.1.1"; +export declare const package_version = "5.1.2"; export declare const supported_fdi: string[]; diff --git a/lib/build/version.js b/lib/build/version.js index b084971..84f1bb3 100644 --- a/lib/build/version.js +++ b/lib/build/version.js @@ -12,5 +12,5 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const package_version = "5.1.1"; +export const package_version = "5.1.2"; export const supported_fdi = ["1.16", "1.17", "1.18", "1.19", "2.0", "3.0"]; diff --git a/lib/ts/version.ts b/lib/ts/version.ts index 4c77d10..85f5c82 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,6 +12,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const package_version = "5.1.1"; +export const package_version = "5.1.2"; export const supported_fdi = ["1.16", "1.17", "1.18", "1.19", "2.0", "3.0"]; diff --git a/package-lock.json b/package-lock.json index 75653a8..7bb782f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-react-native", - "version": "5.1.1", + "version": "5.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-react-native", - "version": "5.1.1", + "version": "5.1.2", "license": "Apache 2.0", "dependencies": { "base-64": "^1.0.0", diff --git a/package.json b/package.json index 691c6b8..76793b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-react-native", - "version": "5.1.1", + "version": "5.1.2", "description": "React Native SDK for SuperTokens", "main": "index.js", "scripts": {