From bb74ebb48dc5505b8aecbfe8c5704297a140217f Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 12 Jul 2024 17:56:24 +0200 Subject: [PATCH 1/2] fix: remove redundant removeToken calls --- lib/src/dio-interceptor-wrapper.dart | 7 - lib/src/front-token.dart | 2 + lib/src/supertokens-http-client.dart | 201 ++++++++++++--------------- 3 files changed, 89 insertions(+), 121 deletions(-) diff --git a/lib/src/dio-interceptor-wrapper.dart b/lib/src/dio-interceptor-wrapper.dart index bf16dbc..966e6ef 100644 --- a/lib/src/dio-interceptor-wrapper.dart +++ b/lib/src/dio-interceptor-wrapper.dart @@ -180,13 +180,6 @@ class SuperTokensInterceptorWrapper extends Interceptor { type: DioExceptionType.unknown, error: e), ); - } finally { - LocalSessionState localSessionState = - await SuperTokensUtils.getLocalSessionState(); - if (localSessionState.status == LocalSessionStateStatus.NOT_EXISTS) { - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); - } } } diff --git a/lib/src/front-token.dart b/lib/src/front-token.dart index 499e41f..ef5b23e 100644 --- a/lib/src/front-token.dart +++ b/lib/src/front-token.dart @@ -4,6 +4,7 @@ import 'package:mutex/mutex.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:supertokens_flutter/src/utilities.dart'; import 'package:supertokens_flutter/supertokens.dart'; +import 'package:supertokens_flutter/src/anti-csrf.dart'; class FrontToken { static String? tokenInMemory; @@ -148,6 +149,7 @@ class FrontToken { await _removeTokenFromStorage(); await Utils.setToken(TokenType.ACCESS, ""); await Utils.setToken(TokenType.REFRESH, ""); + await AntiCSRF.removeToken() if (_tokenInfoMutex.isLocked) { _tokenInfoMutex.release(); } diff --git a/lib/src/supertokens-http-client.dart b/lib/src/supertokens-http-client.dart index 01317e4..0ea4ca9 100644 --- a/lib/src/supertokens-http-client.dart +++ b/lib/src/supertokens-http-client.dart @@ -74,107 +74,98 @@ class Client extends http.BaseClient { return _innerClient.send(customRequest.request); } - try { - while (true) { - await _refreshAPILock.acquireRead(); - // http package does not allow retries with the same request object, so we clone the request when making the network call - http.BaseRequest copiedRequest; - LocalSessionState preRequestLocalSessionState; - http.StreamedResponse response; - try { - copiedRequest = SuperTokensUtils.copyRequest(customRequest.request); - copiedRequest = - await _removeAuthHeaderIfMatchesLocalToken(copiedRequest); - preRequestLocalSessionState = - await SuperTokensUtils.getLocalSessionState(); - String? antiCSRFToken = await AntiCSRF.getToken( - preRequestLocalSessionState.lastAccessTokenUpdate); - - if (antiCSRFToken != null) { - copiedRequest.headers[antiCSRFHeaderKey] = antiCSRFToken; - } - - SuperTokensTokenTransferMethod tokenTransferMethod = - SuperTokens.config.tokenTransferMethod; - copiedRequest.headers["st-auth-mode"] = - tokenTransferMethod.getValue(); - - // Adding Authorization headers - copiedRequest = - await Utils.setAuthorizationHeaderIfRequired(copiedRequest); - - // Add cookies to request headers - String? newCookiesToAdd = await Client.cookieStore - ?.getCookieHeaderStringForRequest(copiedRequest.url); - String? existingCookieHeader = - copiedRequest.headers[HttpHeaders.cookieHeader]; - - // If the request already has a "cookie" header, combine it with persistent cookies - if (existingCookieHeader != null && existingCookieHeader != "") { - copiedRequest.headers[HttpHeaders.cookieHeader] = - _generateCookieHeader(existingCookieHeader, newCookiesToAdd); - } else { - copiedRequest.headers[HttpHeaders.cookieHeader] = - newCookiesToAdd ?? ""; - } - - // http package does not allow retries with the same request object, so we clone the request when making the network call - response = await _innerClient.send(copiedRequest); - await Utils.saveTokenFromHeaders(response); - String? frontTokenInHeaders = response.headers[frontTokenHeaderKey]; - SuperTokensUtils.fireSessionUpdateEventsIfNecessary( - wasLoggedIn: preRequestLocalSessionState.status == - LocalSessionStateStatus.EXISTS, - status: response.statusCode, - frontTokenFromResponse: frontTokenInHeaders, - ); - - // Save cookies from the response - String? setCookieFromResponse = - response.headers[HttpHeaders.setCookieHeader]; - await Client.cookieStore?.saveFromSetCookieHeader( - copiedRequest.url, setCookieFromResponse); - } finally { - _refreshAPILock.release(); + while (true) { + await _refreshAPILock.acquireRead(); + // http package does not allow retries with the same request object, so we clone the request when making the network call + http.BaseRequest copiedRequest; + LocalSessionState preRequestLocalSessionState; + http.StreamedResponse response; + try { + copiedRequest = SuperTokensUtils.copyRequest(customRequest.request); + copiedRequest = + await _removeAuthHeaderIfMatchesLocalToken(copiedRequest); + preRequestLocalSessionState = + await SuperTokensUtils.getLocalSessionState(); + String? antiCSRFToken = await AntiCSRF.getToken( + preRequestLocalSessionState.lastAccessTokenUpdate); + + if (antiCSRFToken != null) { + copiedRequest.headers[antiCSRFHeaderKey] = antiCSRFToken; } - if (response.statusCode == SuperTokens.sessionExpiryStatusCode) { - /** - * 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 (customRequest.sessionRefreshAttempts >= - SuperTokens.config.maxRetryAttemptsForSessionRefresh) { - throw SuperTokensException( - "Received a 401 response from ${customRequest.request.url}. Attempted to refresh the session and retry the request with the updated session tokens ${SuperTokens.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."); - } - customRequest.sessionRefreshAttempts++; - - customRequest.request = - await _removeAuthHeaderIfMatchesLocalToken(copiedRequest); - - UnauthorisedResponse shouldRetry = - await onUnauthorisedResponse(preRequestLocalSessionState); - if (shouldRetry.status == UnauthorisedStatus.RETRY) { - // Here we use the original request because it wont contain any of the modifications we make - return await _sendWithRetry(customRequest); - } else { - if (shouldRetry.exception != null) { - throw SuperTokensException(shouldRetry.exception!.message); - } else - return response; - } + SuperTokensTokenTransferMethod tokenTransferMethod = + SuperTokens.config.tokenTransferMethod; + copiedRequest.headers["st-auth-mode"] = + tokenTransferMethod.getValue(); + + // Adding Authorization headers + copiedRequest = + await Utils.setAuthorizationHeaderIfRequired(copiedRequest); + + // Add cookies to request headers + String? newCookiesToAdd = await Client.cookieStore + ?.getCookieHeaderStringForRequest(copiedRequest.url); + String? existingCookieHeader = + copiedRequest.headers[HttpHeaders.cookieHeader]; + + // If the request already has a "cookie" header, combine it with persistent cookies + if (existingCookieHeader != null && existingCookieHeader != "") { + copiedRequest.headers[HttpHeaders.cookieHeader] = + _generateCookieHeader(existingCookieHeader, newCookiesToAdd); } else { - return response; + copiedRequest.headers[HttpHeaders.cookieHeader] = + newCookiesToAdd ?? ""; } + + // http package does not allow retries with the same request object, so we clone the request when making the network call + response = await _innerClient.send(copiedRequest); + await Utils.saveTokenFromHeaders(response); + String? frontTokenInHeaders = response.headers[frontTokenHeaderKey]; + SuperTokensUtils.fireSessionUpdateEventsIfNecessary( + wasLoggedIn: preRequestLocalSessionState.status == + LocalSessionStateStatus.EXISTS, + status: response.statusCode, + frontTokenFromResponse: frontTokenInHeaders, + ); + + // Save cookies from the response + String? setCookieFromResponse = + response.headers[HttpHeaders.setCookieHeader]; + await Client.cookieStore?.saveFromSetCookieHeader( + copiedRequest.url, setCookieFromResponse); + } finally { + _refreshAPILock.release(); } - } finally { - LocalSessionState localSessionState = - await SuperTokensUtils.getLocalSessionState(); - if (localSessionState.status == LocalSessionStateStatus.NOT_EXISTS) { - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); + + if (response.statusCode == SuperTokens.sessionExpiryStatusCode) { + /** + * 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 (customRequest.sessionRefreshAttempts >= + SuperTokens.config.maxRetryAttemptsForSessionRefresh) { + throw SuperTokensException( + "Received a 401 response from ${customRequest.request.url}. Attempted to refresh the session and retry the request with the updated session tokens ${SuperTokens.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."); + } + customRequest.sessionRefreshAttempts++; + + customRequest.request = + await _removeAuthHeaderIfMatchesLocalToken(copiedRequest); + + UnauthorisedResponse shouldRetry = + await onUnauthorisedResponse(preRequestLocalSessionState); + if (shouldRetry.status == UnauthorisedStatus.RETRY) { + // Here we use the original request because it wont contain any of the modifications we make + return await _sendWithRetry(customRequest); + } else { + if (shouldRetry.exception != null) { + throw SuperTokensException(shouldRetry.exception!.message); + } else + return response; + } + } else { + return response; } } } @@ -302,28 +293,10 @@ class Client extends http.BaseClient { status: UnauthorisedStatus.API_ERROR, error: SuperTokensException("Some unknown error occured")); } finally { - LocalSessionState localSessionState = - await SuperTokensUtils.getLocalSessionState(); - - if (localSessionState.status == LocalSessionStateStatus.NOT_EXISTS) { - await FrontToken.removeToken(); - await AntiCSRF.removeToken(); - } - _refreshAPILock.release(); } } - static Future clearTokensIfRequired() async { - LocalSessionState preRequestLocalSessionState = - await SuperTokensUtils.getLocalSessionState(); - if (preRequestLocalSessionState.status == - LocalSessionStateStatus.NOT_EXISTS) { - await AntiCSRF.removeToken(); - await FrontToken.removeToken(); - } - } - static String _cookieMapToHeaderString(Map cookieMap) { return cookieMap.keys.map((e) => "$e=${cookieMap[e]}").join(";"); } From 67c67039ffa184ea74fa53c4a16a94f260188431 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 12 Jul 2024 18:26:42 +0200 Subject: [PATCH 2/2] chore: bump version --- CHANGELOG.md | 6 ++++++ lib/src/version.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2580cc2..3dc44fa 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] +## [0.6.1] - 2024-07-12 + +### Changes + +- Removed redundant calls to `removeToken` + ## [0.6.0] - 2024-06-05 ### Changes diff --git a/lib/src/version.dart b/lib/src/version.dart index 97331b2..72266fe 100644 --- a/lib/src/version.dart +++ b/lib/src/version.dart @@ -7,5 +7,5 @@ class Version { "2.0", "3.0" ]; - static String sdkVersion = "0.6.0"; + static String sdkVersion = "0.6.1"; } diff --git a/pubspec.yaml b/pubspec.yaml index 7093f93..7ba9e41 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: supertokens_flutter description: SuperTokens SDK for Flutter apps -version: 0.6.0 +version: 0.6.1 homepage: https://supertokens.com/ repository: https://github.com/supertokens/supertokens-flutter issue_tracker: https://github.com/supertokens/supertokens-flutter/issues