Skip to content

Commit

Permalink
fix: shouldDoInterceptionBasedOnUrl returns true for any valid subdom…
Browse files Browse the repository at this point in the history
…ain (#251)

* fix: shouldDoInterceptionBasedOnUrl returns true for any valid subdomains of the url

* fix: PR changes

* allows interception to be added regardless of port differences

* modifies changelog

---------

Co-authored-by: rishabhpoddar <[email protected]>
  • Loading branch information
anku255 and rishabhpoddar authored May 8, 2024
1 parent 2b3aba8 commit 7ab3bc6
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 105 deletions.
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [20.0.0] - 2024-04-03

### Breaking changes

The `shouldDoInterceptionBasedOnUrl` 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


**Before:**

```javascript
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", "api.example.com") // false
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", ".api.example.com") // true
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", "example.com") // false
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", ".example.com") // true
shouldDoInterceptionBasedOnUrl("https://api.example.com", "", ".example.com:8080") // false
shouldDoInterceptionBasedOnUrl("https://api.example.com", "https://example.com:8080") // false
```

**After:**

```javascript
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", "api.example.com") // true
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", ".api.example.com") // true
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", "example.com") // true
shouldDoInterceptionBasedOnUrl("https://sub.api.example.com", "", ".example.com") // true
shouldDoInterceptionBasedOnUrl("https://api.example.com", "", ".example.com:8080") // true
shouldDoInterceptionBasedOnUrl("https://api.example.com", "https://example.com:8080") // true
```

## [19.0.1] - 2024-03-18
- Fixes test server

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

Large diffs are not rendered by default.

34 changes: 10 additions & 24 deletions lib/build/recipeImplementation.js

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

10 changes: 9 additions & 1 deletion lib/build/utils/index.d.ts

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

51 changes: 33 additions & 18 deletions lib/build/utils/index.js

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

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

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

2 changes: 1 addition & 1 deletion lib/build/version.js

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

36 changes: 11 additions & 25 deletions lib/ts/recipeImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { supported_fdi } from "./version";
import { logDebugMessage } from "./logger";
import { STGeneralError } from "./error";
import { addInterceptorsToXMLHttpRequest } from "./xmlhttprequest";
import { normaliseSessionScopeOrThrowError, normaliseURLDomainOrThrowError } from "./utils";
import { matchesDomainOrSubdomain, normaliseSessionScopeOrThrowError, normaliseURLDomainOrThrowError } from "./utils";
import DateProviderReference from "./utils/dateProvider";

export default function RecipeImplementation(recipeImplInput: {
Expand Down Expand Up @@ -270,12 +270,6 @@ export default function RecipeImplementation(recipeImplInput: {
" sessionTokenBackendDomain: " +
sessionTokenBackendDomain
);
function isNumeric(str: any) {
if (typeof str != "string") return false; // we only process strings!
return (
!isNaN(str as any) && !isNaN(parseFloat(str)) // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
); // ...and ensure strings of whitespace fail
}

// The safest/best way to add this is the hash as the browser strips it before sending
// but we don't have a reason to limit checking to that part.
Expand All @@ -286,28 +280,20 @@ export default function RecipeImplementation(recipeImplInput: {
toCheckUrl = normaliseURLDomainOrThrowError(toCheckUrl);
let urlObj = new URL(toCheckUrl);
let domain = urlObj.hostname;
if (sessionTokenBackendDomain === undefined) {
domain = urlObj.port === "" ? domain : domain + ":" + urlObj.port;
let apiDomainAndInputDomainMatch = false;
if (apiDomain !== "") {
// we have the "" check cause in tests, we pass "" in lots of cases.
apiDomain = normaliseURLDomainOrThrowError(apiDomain);
let apiUrlObj = new URL(apiDomain);
return (
domain === (apiUrlObj.port === "" ? apiUrlObj.hostname : apiUrlObj.hostname + ":" + apiUrlObj.port)
);
apiDomainAndInputDomainMatch = domain === apiUrlObj.hostname;
}
if (sessionTokenBackendDomain === undefined || apiDomainAndInputDomainMatch) {
// even if sessionTokenBackendDomain !== undefined, if there is an exact match
// of api domain, ignoring the port, we return true
return apiDomainAndInputDomainMatch;
} else {
let normalisedsessionDomain = normaliseSessionScopeOrThrowError(sessionTokenBackendDomain);
if (sessionTokenBackendDomain.split(":").length > 1) {
// means port may provided
let portStr = sessionTokenBackendDomain.split(":")[sessionTokenBackendDomain.split(":").length - 1];
if (isNumeric(portStr)) {
normalisedsessionDomain += ":" + portStr;
domain = urlObj.port === "" ? domain : domain + ":" + urlObj.port;
}
}
if (sessionTokenBackendDomain.startsWith(".")) {
return ("." + domain).endsWith(normalisedsessionDomain);
} else {
return domain === normalisedsessionDomain;
}
return matchesDomainOrSubdomain(domain, normalisedsessionDomain);
}
},

Expand Down
51 changes: 33 additions & 18 deletions lib/ts/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,41 +34,36 @@ export function normaliseURLPathOrThrowError(input: string): string {
return new NormalisedURLPath(input).getAsStringDangerous();
}

export function normaliseSessionScopeOrThrowError(sessionTokenFrontendDomain: string): string {
function helper(sessionTokenFrontendDomain: string): string {
sessionTokenFrontendDomain = sessionTokenFrontendDomain.trim().toLowerCase();
export function normaliseSessionScopeOrThrowError(sessionScope: string): string {
function helper(sessionScope: string): string {
sessionScope = sessionScope.trim().toLowerCase();

// first we convert it to a URL so that we can use the URL class
if (sessionTokenFrontendDomain.startsWith(".")) {
sessionTokenFrontendDomain = sessionTokenFrontendDomain.substr(1);
if (sessionScope.startsWith(".")) {
sessionScope = sessionScope.substr(1);
}

if (!sessionTokenFrontendDomain.startsWith("http://") && !sessionTokenFrontendDomain.startsWith("https://")) {
sessionTokenFrontendDomain = "http://" + sessionTokenFrontendDomain;
if (!sessionScope.startsWith("http://") && !sessionScope.startsWith("https://")) {
sessionScope = "http://" + sessionScope;
}

try {
let urlObj = new URL(sessionTokenFrontendDomain);
sessionTokenFrontendDomain = urlObj.hostname;
let urlObj = new URL(sessionScope);
sessionScope = urlObj.hostname;

// remove leading dot
if (sessionTokenFrontendDomain.startsWith(".")) {
sessionTokenFrontendDomain = sessionTokenFrontendDomain.substr(1);
}

return sessionTokenFrontendDomain;
return sessionScope;
} catch (err) {
throw new Error("Please provide a valid sessionTokenFrontendDomain");
throw new Error("Please provide a valid sessionScope");
}
}

let noDotNormalised = helper(sessionTokenFrontendDomain);
let noDotNormalised = helper(sessionScope);

if (noDotNormalised === "localhost" || isAnIpAddress(noDotNormalised)) {
return noDotNormalised;
}

if (sessionTokenFrontendDomain.startsWith(".")) {
if (sessionScope.startsWith(".")) {
return "." + noDotNormalised;
}

Expand Down Expand Up @@ -169,3 +164,23 @@ export function getNormalisedUserContext(userContext?: any): any {

return userContext;
}

/**
* Checks if a given string matches any subdomain or the main domain of a specified hostname.
*
* @param {string} hostname - The hostname to derive subdomains from.
* @param {string} str - The string to compare against the subdomains.
* @returns {boolean} True if the string matches any subdomain or the main domain, otherwise false.
*/
export function matchesDomainOrSubdomain(hostname: string, str: string): boolean {
const parts = hostname.split(".");

for (let i = 0; i < parts.length; i++) {
const subdomainCandidate = parts.slice(i).join(".");
if (subdomainCandidate === str || `.${subdomainCandidate}` === str) {
return true;
}
}

return false;
}
2 changes: 1 addition & 1 deletion lib/ts/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
export const package_version = "19.0.1";
export const package_version = "20.0.0";

export const supported_fdi = ["1.16", "1.17", "1.18", "1.19"];
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "supertokens-website",
"version": "19.0.1",
"version": "20.0.0",
"description": "frontend sdk for website to be used for auth solution.",
"main": "index.js",
"dependencies": {
Expand Down
Loading

0 comments on commit 7ab3bc6

Please sign in to comment.