diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c8687..f8fbb9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.5.0] - 2024-05-08 + +### Breaking changes + +The `shouldDoInterceptions` function now returns true: +- If `sessionTokenBackendDomain` is a valid subdomain of the URL's domain. This aligns with the behavior of browsers when sending cookies to subdomains. +- Even if the ports of the URL you are querying are different compared to the `apiDomain`'s port ot the `sessionTokenBackendDomain` port (as long as the hostname is the same, or a subdomain of the `sessionTokenBackendDomain`): https://github.com/supertokens/supertokens-website/issues/217 + + ## [0.4.0] - 2024-03-25 - Relaxes dependency on `http` to be anything below `2.0.0` diff --git a/lib/src/utilities.dart b/lib/src/utilities.dart index fc68522..bc7b190 100644 --- a/lib/src/utilities.dart +++ b/lib/src/utilities.dart @@ -187,51 +187,33 @@ class Utils { } var domain = hostname; + var apiDomainAndInputDomainMatch = false; - if (cookieDomain == null) { - domain = [80, 443, 0].contains(urlObject.port) - ? domain.contains(urlObject.port.toString()) - ? hostname + ":${urlObject.port}" - : hostname - : hostname + ":${urlObject.port}"; - + if (!apiDomain.isEmpty) { _apiDomain = NormalisedURLDomain(apiDomain).value; - Uri apiUrlObject; - String apiHostName; - try { - apiUrlObject = Uri.parse(_apiDomain); - apiHostName = apiUrlObject.host; - } catch (e) { - throw SuperTokensException(e.toString()); - } - - String temp = [80, 443, 0].contains(apiUrlObject.port) - ? apiHostName.contains(apiUrlObject.port.toString()) - ? apiHostName + ":${apiUrlObject.port}" - : apiHostName - : apiHostName + ":${apiUrlObject.port}"; + apiDomainAndInputDomainMatch = domain == _apiDomain; + } - return domain == temp; + if (cookieDomain == null || apiDomainAndInputDomainMatch) { + return apiDomainAndInputDomainMatch; } else { String normalisedCookieDomain = NormalisedInputType.normaliseSessionScopeOrThrowError(cookieDomain); - if (cookieDomain.split(":").length > 1) { - String portString = - cookieDomain.split(':')[cookieDomain.split(':').length - 1]; - if (![80, 443, 0].contains(portString)) { - normalisedCookieDomain = normalisedCookieDomain + ':' + portString; - domain = urlObject.port == null - ? domain - : domain + ':' + urlObject.port.toString(); - } - } + return matchesDomainOrSubdomain(domain, normalisedCookieDomain); + } + } - if (cookieDomain.startsWith('.')) { - return ("." + domain).endsWith(normalisedCookieDomain); - } else { - return domain == normalisedCookieDomain; + static bool matchesDomainOrSubdomain(String hostname, String str) { + List parts = hostname.split("."); + + for (int i = 0; i < parts.length; i++) { + String subdomainCandidate = parts.sublist(i).join("."); + if (subdomainCandidate == str || ".$subdomainCandidate" == str) { + return true; } } + + return false; } static bool doesUrlHavePort(Uri uri) { @@ -429,7 +411,7 @@ class NormalisedInputType { } static String sessionScopeHelper(String SessionScope) { - String trimmedSessionScope = SessionScope.trim(); + String trimmedSessionScope = SessionScope.trim().toLowerCase(); if (trimmedSessionScope.startsWith('.')) { trimmedSessionScope = trimmedSessionScope.substring(1); } @@ -443,9 +425,6 @@ class NormalisedInputType { Uri url = Uri.parse(trimmedSessionScope); String host = url.host; trimmedSessionScope = host; - if (trimmedSessionScope.startsWith('.')) { - trimmedSessionScope = trimmedSessionScope.substring(1); - } return trimmedSessionScope; } catch (e) { throw SuperTokensException("Please provide a valid SessionScope"); diff --git a/lib/src/version.dart b/lib/src/version.dart index 99e1444..fe7404c 100644 --- a/lib/src/version.dart +++ b/lib/src/version.dart @@ -1,4 +1,4 @@ class Version { static List supported_fdi = ["1.16", "1.17", "1.18", "1.19"]; - static String sdkVersion = "0.4.0"; + static String sdkVersion = "0.5.0"; } diff --git a/pubspec.yaml b/pubspec.yaml index 6c0dc9e..52768eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: supertokens_flutter description: SuperTokens SDK for Flutter apps -version: 0.4.0 +version: 0.5.0 homepage: https://supertokens.com/ repository: https://github.com/supertokens/supertokens-flutter issue_tracker: https://github.com/supertokens/supertokens-flutter/issues diff --git a/test/config_test.dart b/test/config_test.dart index aee9349..a631214 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:supertokens_flutter/src/normalised-url-domain.dart'; import 'package:supertokens_flutter/src/normalised-url-path.dart'; +import 'package:supertokens_flutter/src/utilities.dart'; void main() { group('Normalise URL Path :: ', () { @@ -408,4 +409,346 @@ void main() { expect(out, "http://localhost.org:8080"); }); }); + + test("shouldDoInterceptionTests", () { + // true cases without cookieDomain + expect( + true, + Utils.shouldDoInterceptions( + "api.example.com", "https://api.example.com", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://api.example.com", "http://api.example.com", null)); + expect( + true, + Utils.shouldDoInterceptions( + "api.example.com", "http://api.example.com", null)); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.com", "http://api.example.com", null)); + expect( + true, + Utils.shouldDoInterceptions("https://api.example.com:3000", + "http://api.example.com:3000", null)); + expect(true, + Utils.shouldDoInterceptions("localhost:3000", "localhost:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "https://localhost:3000", "https://localhost:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "https://localhost:3000", "https://localhost:3001", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost:3000", "http://localhost:3001", null)); + expect(true, + Utils.shouldDoInterceptions("localhost:3000", "localhost:3001", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost:3000", "http://localhost:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "localhost:3000", "https://localhost:3000", null)); + expect(true, + Utils.shouldDoInterceptions("localhost", "https://localhost", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost:3000", "https://localhost:3000", null)); + expect(true, + Utils.shouldDoInterceptions("127.0.0.1:3000", "127.0.0.1:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "https://127.0.0.1:3000", "https://127.0.0.1:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1:3000", "http://127.0.0.1:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "127.0.0.1:3000", "https://127.0.0.1:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1:3000", "https://127.0.0.1:3000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1", "https://127.0.0.1", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "localhost.org", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "http://localhost.org", null)); + + // true cases with cookieDomain + expect(true, + Utils.shouldDoInterceptions("api.example.com", "", "api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "http://api.example.com", "", "http://api.example.com")); + expect(true, + Utils.shouldDoInterceptions("api.example.com", "", ".example.com")); + expect(true, + Utils.shouldDoInterceptions("api.example.com", "", "example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.com", "", "http://api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.com", "", "https://api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", ".sub.api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", "sub.api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", ".api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", "api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", ".example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com", "", "example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", ".example.com:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", "example.com:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", ".example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", "example.com")); + expect( + true, + Utils.shouldDoInterceptions("https://sub.api.example.com:3000", "", + "https://sub.api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.com:3000", "", ".api.example.com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.com:3000", "", "api.example.com")); + expect(true, + Utils.shouldDoInterceptions("localhost:3000", "", "localhost:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "https://localhost:3000", "", ".localhost:3000")); + expect(true, Utils.shouldDoInterceptions("localhost", "", "localhost")); + expect( + true, + Utils.shouldDoInterceptions( + "http://a.localhost:3000", "", ".localhost:3000")); + expect(true, + Utils.shouldDoInterceptions("127.0.0.1:3000", "", "127.0.0.1:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "https://127.0.0.1:3000", "", "https://127.0.0.1:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1:3000", "", "http://127.0.0.1:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "127.0.0.1:3000", "", "https://127.0.0.1:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1:3000", "", "https://127.0.0.1:3000")); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1", "", "https://127.0.0.1")); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "", ".localhost.org")); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "", "localhost.org")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", ".com")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.co.uk:3000", "", ".api.example.co.uk")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub1.api.example.co.uk:3000", "", ".api.example.co.uk")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.co.uk:3000", "", ".api.example.co.uk")); + expect( + true, + Utils.shouldDoInterceptions( + "https://api.example.co.uk:3000", "", "api.example.co.uk")); + expect(true, + Utils.shouldDoInterceptions("localhost:3000", "localhost:8080", null)); + expect( + true, Utils.shouldDoInterceptions("localhost:3001", "localhost", null)); + expect( + true, + Utils.shouldDoInterceptions("https://api.example.com:3002", + "https://api.example.com:3001", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "localhost.org:2000", null)); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost.org", "localhost", "localhost.org")); + expect(true, + Utils.shouldDoInterceptions("localhost", "localhost", "localhost.org")); + expect( + true, Utils.shouldDoInterceptions("localhost", "", "localhost:8080")); + expect( + true, + Utils.shouldDoInterceptions( + "http://localhost:80", "", "localhost:8080")); + expect(true, + Utils.shouldDoInterceptions("localhost:3000", "", "localhost:8080")); + expect( + true, + Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", ".example.com:3001")); + expect( + true, + Utils.shouldDoInterceptions( + "http://127.0.0.1:3000", "", "https://127.0.0.1:3010")); + + // false cases with api + expect( + true, !Utils.shouldDoInterceptions("localhost", "localhost.org", null)); + expect(true, + !Utils.shouldDoInterceptions("google.com", "localhost.org", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "http://google.com", "localhost.org", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://google.com", "localhost.org", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://google.com:8080", "localhost.org", null)); + expect(true, + !Utils.shouldDoInterceptions("localhost:3001", "example.com", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://example.com", "https://api.example.com", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://api.example.com", "https://a.api.example.com", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://api.example.com", "https://a.api.example.com:3000", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://api.example.com", "https://example.com", null)); + expect( + true, + !Utils.shouldDoInterceptions( + "https://example.com:3001", "https://api.example.com:3001", null)); + + // false cases with cookieDomain + expect( + true, !Utils.shouldDoInterceptions("localhost", "", "localhost.org")); + expect( + true, !Utils.shouldDoInterceptions("google.com", "", "localhost.org")); + expect(true, + !Utils.shouldDoInterceptions("http://google.com", "", "localhost.org")); + expect( + true, + !Utils.shouldDoInterceptions( + "https://google.com", "", "localhost.org")); + expect( + true, + !Utils.shouldDoInterceptions( + "https://google.com:8080", "", "localhost.org")); + expect( + true, + !Utils.shouldDoInterceptions( + "https://api.example.com:3000", "", ".a.api.example.com")); + expect( + true, + !Utils.shouldDoInterceptions( + "https://sub.api.example.com:3000", "", "localhost")); + expect(true, + !Utils.shouldDoInterceptions("http://localhost.org", "", "localhost")); + expect(true, + !Utils.shouldDoInterceptions("http://localhost.org", "", ".localhost")); + expect( + true, + !Utils.shouldDoInterceptions( + "http://localhost.org", "", "localhost:2000")); + + // errors in input + try { + Utils.shouldDoInterceptions("/some/path", "", "api.example.co.uk"); + expect(true, false); + } on Exception catch (err) { + if (err.toString() != "Please provide a valid domain name") { + throw err; + } + } + try { + expect(true, + Utils.shouldDoInterceptions("/some/path", "api.example.co.uk", null)); + expect(true, false); + } on Exception catch (err) { + if (err.toString() != "Please provide a valid domain name") { + throw err; + } + } + }); }