Skip to content

Commit

Permalink
Merge pull request #64 from supertokens/feat/add-support-for-debug-lo…
Browse files Browse the repository at this point in the history
…gging

feat: add support for debug logging
  • Loading branch information
rishabhpoddar authored Sep 30, 2024
2 parents e6684c3 + 2686977 commit a40eff1
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/pre-commit-hook-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ jobs:
pr-title:
name: Pre commit hook check
runs-on: ubuntu-latest
container: rishabhpoddar/supertokens_flutter_sdk_testing
steps:
- uses: actions/checkout@v2
- name: Make a dummy change to README.md
run: |
echo "# Dummy change for PR check" >> README.md
- run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action'
- run: ./hooks/pre-commit.sh
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.6.2] - 2024-09-27

- Adds support for debug logs using a `debug` option in the init() method of the SDK.

## [0.6.1] - 2024-07-12

### Changes
Expand Down
14 changes: 14 additions & 0 deletions lib/src/anti-csrf.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supertokens_flutter/src/logger.dart';

class _AntiCSRFInfo {
String? antiCSRF;
Expand All @@ -15,12 +16,16 @@ class AntiCSRF {
static String _sharedPreferencesKey = "supertokens-flutter-anti-csrf";

static Future<String?> getToken(String? associatedAccessTokenUpdate) async {
logDebugMessage('AntiCSRF.getToken: Getting token');
logDebugMessage('AntiCSRF.getToken: associatedAccessTokenUpdate: ${associatedAccessTokenUpdate}');
if (associatedAccessTokenUpdate == null) {
logDebugMessage('AntiCSRF.getToken: associated token is null');
AntiCSRF._antiCSRFInfo = null;
return null;
}

if (AntiCSRF._antiCSRFInfo == null) {
logDebugMessage('AntiCSRF.getToken: antiCSRFInfo is null');
SharedPreferences preferences = await SharedPreferences.getInstance();
String? antiCSRFToken =
preferences.getString(AntiCSRF._sharedPreferencesKey);
Expand All @@ -34,6 +39,7 @@ class AntiCSRF {
} else if (AntiCSRF._antiCSRFInfo?.associatedAccessTokenUpdate != null &&
AntiCSRF._antiCSRFInfo?.associatedAccessTokenUpdate !=
associatedAccessTokenUpdate) {
logDebugMessage('AntiCSRF.getToken: associatedAccessTokenUpdate did not match');
AntiCSRF._antiCSRFInfo = null;
return await AntiCSRF.getToken(associatedAccessTokenUpdate);
}
Expand All @@ -43,24 +49,32 @@ class AntiCSRF {

static Future<void> setToken(
String antiCSRFToken, String? associatedAccessTokenUpdate) async {
logDebugMessage('AntiCSRF.setToken: Setting token');
logDebugMessage('AntiCSRF.setToken: associatedAccessTokenUpdate: ${associatedAccessTokenUpdate}');
if (associatedAccessTokenUpdate == null) {
logDebugMessage('AntiCSRF.setToken: associated token is null');
AntiCSRF._antiCSRFInfo = null;
return;
}

SharedPreferences preferences = await SharedPreferences.getInstance();
logDebugMessage('AntiCSRF.setToken: setting preferences for: ${AntiCSRF._sharedPreferencesKey}');
await preferences.setString(AntiCSRF._sharedPreferencesKey, antiCSRFToken);
await preferences.reload();

logDebugMessage('AntiCSRF.setToken: storing the token before returning');
AntiCSRF._antiCSRFInfo = _AntiCSRFInfo(
antiCSRFToken: antiCSRFToken,
associatedAccessTokenUpdate: associatedAccessTokenUpdate);
}

static Future<void> removeToken() async {
logDebugMessage('AntiCSRF.removeToken: Removing token');
logDebugMessage('AntiCSRF.removeToken: Updating preferences');
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.remove(AntiCSRF._sharedPreferencesKey);
await preferences.reload();
logDebugMessage('AntiCSRF.removeToken: Setting token to null');
AntiCSRF._antiCSRFInfo = null;
}
}
36 changes: 36 additions & 0 deletions lib/src/cookie-store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:io';

import 'package:shared_preferences/shared_preferences.dart';
import 'package:supertokens_flutter/src/logger.dart';

class SuperTokensCookieStore {
static Map<Uri, List<Cookie>>? _allCookies;
Expand All @@ -24,10 +25,12 @@ class SuperTokensCookieStore {

/// Loads all cookies stored in shared preferences into the in memory map [_allCookies]
static Future<void> _loadFromPersistence() async {
logDebugMessage('SuperTokensCookieStore._loadFromPersistence: Trying to load cookies from memory');
_allCookies = {};
String cookiesStringInStorage =
_sharedPreferences?.getString(_cookieSharedPrefsKey) ?? "{}";
Map<String, dynamic> cookiesInStorage = jsonDecode(cookiesStringInStorage);
logDebugMessage('SuperTokensCookieStore._loadFromPersistence: cookies found: ${jsonEncode(cookiesInStorage)}');
cookiesInStorage.forEach((key, value) {
Uri uri = Uri.parse(key);
List<String> cookieStrings = List.from(value);
Expand All @@ -49,8 +52,11 @@ class SuperTokensCookieStore {
///
/// If you are trying to store cookies from a "set-cookie" header response, consider using the [saveFromSetCookieHeader] utility method which parses the header string.
Future<void> saveFromResponse(Uri uri, List<Cookie> cookies) async {
logDebugMessage('SuperTokensCookieStore.saveFromResponse: Saving cookies against: ${uri}');
logDebugMessage('SuperTokensCookieStore.saveFromResponse: Passed cookies: ${jsonEncode(cookies)}');
await Future.forEach<Cookie>(cookies, (element) async {
Uri uriToStore = await _getCookieUri(uri, element);
logDebugMessage('SuperTokensCookieStore.saveFromResponse: Setting based on uriToStore: ${uriToStore}');
List<Cookie> currentCookies = _allCookies?[uriToStore] ?? List.from([]);
currentCookies = currentCookies
// ignore: unnecessary_null_comparison
Expand All @@ -68,6 +74,7 @@ class SuperTokensCookieStore {
_allCookies?[uriToStore] = currentCookies;
});

logDebugMessage('SuperTokensCookieStore.saveFromResponse: Removing empty cookies');
_allCookies?.removeWhere((key, value) => value.isEmpty);

await _updatePersistentStorage();
Expand All @@ -76,14 +83,17 @@ class SuperTokensCookieStore {

/// Returns a Uri to use when saving the cookie
Future<Uri> _getCookieUri(Uri requestUri, Cookie cookie) async {
logDebugMessage('SuperTokensCookieStore._getCookieUri: Creating cookie uri from: ${requestUri}');
Uri cookieUri = Uri.parse(
// ignore: unnecessary_null_comparison
"${requestUri.scheme == null ? "http" : requestUri.scheme}://${requestUri.host}${cookie.path == null ? "" : cookie.path}");

if (cookie.domain != null) {
String domain = cookie.domain ?? "";
logDebugMessage('SuperTokensCookiesStore._getCookieUri: got domain: ${domain}');
if (domain[0] == ".") {
domain = domain.substring(1);
logDebugMessage('SuperTokensCookiesStore._getCookieUri: Using domain substring: ${domain}');
}

try {
Expand All @@ -98,22 +108,27 @@ class SuperTokensCookieStore {
}
}

logDebugMessage('Generated cookie uri: ${cookieUri}');
return cookieUri;
}

/// Uses the [_allCookies] map to update values in shared preferences.
///
/// Strips expired cookies before storing in shared preferences
Future<void> _updatePersistentStorage() async {
logDebugMessage('SuperTokensCookieStore._updatePersistentStorage: Updating persistent storage with cookies');
Map<String, List<String>> mapToStore = {};
_allCookies?.forEach((key, value) {
String uriString = key.toString();
List<String> cookieStrings =
List.from(value.map((e) => e.toString()).toList());
logDebugMessage('SuperTokensCookieStore._updatePersistentStorage: Setting to ${uriString}');
logDebugMessage('SuperTokensCookieStore._updatePersistentStorage: Setting value ${cookieStrings}');
mapToStore[uriString] = cookieStrings;
});

String stringToStore = jsonEncode(mapToStore);
logDebugMessage('SuperTokensCookieStore._updatePersistentStorage: Storing preferences: ${stringToStore}');
await _sharedPreferences?.setString(_cookieSharedPrefsKey, stringToStore);
}

Expand All @@ -123,10 +138,12 @@ class SuperTokensCookieStore {
///
/// If you are trying to add cookies to a "cookie" header for a network call, consider using the [getCookieHeaderStringForRequest] which creates a semi-colon separated cookie string for a given Uri.
Future<List<Cookie>> getForRequest(Uri uri) async {
logDebugMessage('SuperTokensCookieStore.getForRequest: Getting cookies for request from uri: ${uri}');
List<Cookie> cookiesToReturn = [];
List<Cookie> allValidCookies = [];

if (_allCookies == null) {
logDebugMessage('SuperTokensCookieStore.getForRequest: No cookies found');
return cookiesToReturn;
}

Expand All @@ -137,6 +154,7 @@ class SuperTokensCookieStore {
allValidCookies.addAll(storedCookies);
}
}
logDebugMessage('SuperTokensCookieStore.getForRequest: Valid cookies found: ${allValidCookies.length}');

if (allValidCookies.isNotEmpty) {
List<Cookie> cookiesToRemoveFromStorage = [];
Expand All @@ -150,21 +168,29 @@ class SuperTokensCookieStore {
}
});

logDebugMessage('SuperTokensCookieStore.getForRequest: Total cookies to remove: ${cookiesToRemoveFromStorage.length}');
if (cookiesToRemoveFromStorage.isNotEmpty) {
await _removeFromPersistence(uri, cookiesToRemoveFromStorage);
}
}

logDebugMessage('SuperTokensCookieStore.getForRequest: Total cookies found ${cookiesToReturn.length}');
return cookiesToReturn;
}

/// Checks whether a network request's domain can be considered valid for a cookie to be sent
bool _doesDomainMatch(String cookieHost, String requestHost) {
logDebugMessage('SuperTokensCookieStore._doesDomainMatch: Determining if domain matches');
logDebugMessage('SuperTokensCookieStore._doesDomainMatch: cookiesHost: ${cookieHost}');
logDebugMessage('SuperTokensCookieStore._doesDomainMatch: requestHost: ${requestHost}');
return requestHost == cookieHost || requestHost.endsWith(".$cookieHost");
}

/// Checks whether a network request's path can be considered valid for a cookie to be sent
bool _doesPathMatch(String cookiePath, String requestPath) {
logDebugMessage('SuperTokensCookieStore._doesPathMatch: Determining if path matches');
logDebugMessage('SuperTokensCookieStore._doesPathMatch: cookiePath: ${cookiePath}');
logDebugMessage('SuperTokensCookieStore._doesPathMatch: requestPath: ${requestPath}');
return (requestPath == cookiePath) ||
(requestPath.startsWith(cookiePath) &&
cookiePath[cookiePath.length - 1] == "/") ||
Expand All @@ -175,9 +201,13 @@ class SuperTokensCookieStore {
/// Removes a list of cookies from persistent storage
Future<void> _removeFromPersistence(
Uri uri, List<Cookie> cookiesToRemove) async {
logDebugMessage('SuperTokensCookieStore._removeFromPersistence: Removing cookies from persistent storage');
logDebugMessage('SuperTokensCookieStore._removeFromPersistence: Total cookies to remove: ${cookiesToRemove.length}');
logDebugMessage('SuperTokensCookieStore._removeFromPersistence: uri: ${uri}');
List<Cookie> _cookiesToRemove = List.from(cookiesToRemove);
List<Cookie> currentCookies = _allCookies?[uri] ?? List.from([]);

logDebugMessage('SuperTokensCookieStore._removeFromPersistence: Removing each cookie');
_cookiesToRemove.forEach((element) {
currentCookies.remove(element);
});
Expand All @@ -192,7 +222,9 @@ class SuperTokensCookieStore {
///
/// Does not return expired cookies and will remove them from persistent storage if any are found.
Future<String> getCookieHeaderStringForRequest(Uri uri) async {
logDebugMessage('SuperTokensCookieStore.getCookieHeaderStringForRequest: Getting cookie header for request from uri: ${uri}');
List<Cookie> cookies = await getForRequest(uri);
logDebugMessage('SuperTokensCookieStore.getCookieHeaderStringForRequest: Total cookies found: ${cookies.length}');
// ignore: unnecessary_null_comparison
if (cookies != null && cookies.isNotEmpty) {
List<String> cookiesStringList =
Expand All @@ -201,6 +233,7 @@ class SuperTokensCookieStore {
return cookieHeaderString;
}

logDebugMessage('SuperTokensCookieStore.getCookieHeaderStringForRequest: Returning empty value');
return "";
}

Expand All @@ -214,16 +247,19 @@ class SuperTokensCookieStore {
///
/// Expired cookies are not saved.
Future<void> saveFromSetCookieHeader(Uri uri, String? setCookieHeader) async {
logDebugMessage('SuperTokensCookieStore.saveFromSetCookieHeader: Saving cookie from header against uri: ${uri}');
if (setCookieHeader != null) {
await saveFromResponse(uri, getCookieListFromHeader(setCookieHeader));
}
}

static List<Cookie> getCookieListFromHeader(String setCookieHeader) {
logDebugMessage('SuperTokensCookieStore.getCookieListFromHeader: Getting cookie list from header: ${setCookieHeader}');
List<String> setCookiesStringList =
setCookieHeader.split(RegExp(r',(?=[^ ])'));
List<Cookie> setCookiesList =
setCookiesStringList.map((e) => Cookie.fromSetCookieValue(e)).toList();
logDebugMessage('SuperTokensCookieStore.getCookieListFromHeader: Total cookies found in header: ${setCookiesList.length}');
return setCookiesList;
}
}
Loading

0 comments on commit a40eff1

Please sign in to comment.