Skip to content

Commit b00afa2

Browse files
anku255porcellus
andauthored
feat: add dateProvider config (#784)
* Add dateProvider config * chore: update CHANGELOG.md * PR changes * Fix tests * PR changes * Update Changelog * update dependencies version --------- Co-authored-by: Mihály Lengyel <[email protected]>
1 parent d2ca50e commit b00afa2

19 files changed

+325
-24
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
77

8+
## [0.37.0] - 2024-01-15
9+
10+
## Breaking Changes
11+
12+
- The default `DateProvider` implementation relies on `localStorage`. If your environment lacks support for `localStorage`, you must provide custom implementations for either the `DateProvider` or `localStorage`.
13+
14+
### Changes
15+
16+
- Exporting the `DateProvider` from supertokens-web-js, that both built-in and custom validators can use instead of `Date.now` to get an estimate of the server clock.
17+
- Added the `dateProvider` prop to the configuration that can be used to customize the built-in `DateProvider`.
18+
- Added `calculateClockSkewInMillis` as an overrideable function to the Session recipe that estimates the time difference between the backend and the client.
19+
820
## [0.36.1] - 2023-12-20
921

1022
### Fixes

lib/build/dateProvider/index.d.ts

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/build/dateProvider/types.d.ts

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/build/genericComponentOverrideContext.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/build/types.d.ts

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/build/version.d.ts

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ts/dateProvider/index.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
export { DateProviderReference } from "supertokens-web-js/utils/dateProvider";

lib/ts/dateProvider/types.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
export { DateProviderInput, DateProviderInterface } from "supertokens-web-js/utils/dateProvider/types";

lib/ts/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* under the License.
1414
*/
1515

16+
import type { DateProviderInput } from "./dateProvider/types";
1617
import type { BaseRecipeModule } from "./recipe/recipeModule/baseRecipeModule";
1718
import type { NormalisedConfig as NormalisedRecipeModuleConfig } from "./recipe/recipeModule/types";
1819
import type { TranslationFunc, TranslationStore } from "./translation/translationHelpers";
@@ -72,6 +73,8 @@ export type SuperTokensConfig = {
7273

7374
windowHandler?: WindowHandlerInput;
7475

76+
dateProvider?: DateProviderInput;
77+
7578
usesDynamicLoginMethods?: boolean;
7679

7780
languageTranslations?: {

lib/ts/version.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
* License for the specific language governing permissions and limitations
1313
* under the License.
1414
*/
15-
export const package_version = "0.36.1";
15+
export const package_version = "0.37.0";

package-lock.json

+17-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "supertokens-auth-react",
3-
"version": "0.36.1",
3+
"version": "0.37.0",
44
"description": "ReactJS SDK that provides login functionality with SuperTokens.",
55
"main": "./index.js",
66
"engines": {
@@ -83,7 +83,7 @@
8383
"peerDependencies": {
8484
"react": ">=16.8.0",
8585
"react-dom": ">=16.8.0",
86-
"supertokens-web-js": "^0.8.0"
86+
"supertokens-web-js": "^0.9.0"
8787
},
8888
"scripts": {
8989
"init": "bash ./init.sh",
@@ -109,7 +109,7 @@
109109
"build-pretty": "npm run build && npm run pretty && npm run pretty",
110110
"lint": "node other/checkTranslationKeys.js && cd lib && eslint ./ts --ext .ts,.tsx",
111111
"lint-fix": "cd lib && npx eslint ./ts --ext .ts,.tsx --fix",
112-
"prune": "cd lib && ts-prune | grep -vE 'used in module| - default$| - package_version$|getSuperTokensRoutesForReactRouterDom$|supertokens-web-js|supertokens-website' && exit 1 || exit 0",
112+
"prune": "cd lib && ts-prune | grep -vE 'used in module| - default$| - package_version$|getSuperTokensRoutesForReactRouterDom$|supertokens-web-js|supertokens-website|dateProvider' && exit 1 || exit 0",
113113
"pretty-check": "npx pretty-quick --staged --check .",
114114
"set-up-hooks": "cp hooks/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit",
115115
"build-docs": "rm -rf ./docs && npx typedoc --out ./docs --tsconfig ./lib/tsconfig.json ./lib/ts/index.ts ./lib/ts/**/index.ts ./lib/ts/**/*/index.ts",

test/end-to-end/emailverification.test.js

+78
Original file line numberDiff line numberDiff line change
@@ -925,3 +925,81 @@ describe("Email verification signOut errors", function () {
925925
assert.strictEqual(await error.evaluate((e) => e.textContent), SOMETHING_WENT_WRONG_ERROR);
926926
});
927927
});
928+
929+
describe("Email verification claim refresh with clock skew", function () {
930+
let browser;
931+
let page;
932+
before(async function () {
933+
await backendBeforeEach();
934+
935+
await fetch(`${TEST_SERVER_BASE_URL}/startst`, {
936+
method: "POST",
937+
headers: { "content-type": "application/json" },
938+
body: JSON.stringify({
939+
coreConfig: {
940+
access_token_validity: 2 * 60 * 60, // 2 hours
941+
},
942+
}),
943+
}).catch(console.error);
944+
945+
browser = await puppeteer.launch({
946+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
947+
headless: true,
948+
});
949+
});
950+
951+
after(async function () {
952+
await browser.close();
953+
await fetch(`${TEST_SERVER_BASE_URL}/after`, {
954+
method: "POST",
955+
}).catch(console.error);
956+
await fetch(`${TEST_SERVER_BASE_URL}/stopst`, {
957+
method: "POST",
958+
}).catch(console.error);
959+
});
960+
961+
afterEach(function () {
962+
return screenshotOnFailure(this, browser);
963+
});
964+
965+
beforeEach(async function () {
966+
page = await browser.newPage();
967+
await clearBrowserCookiesWithoutAffectingConsole(page, []);
968+
await Promise.all([
969+
page.goto(`${TEST_CLIENT_BASE_URL}/auth?mode=REQUIRED`),
970+
page.waitForNavigation({ waitUntil: "networkidle0" }),
971+
]);
972+
});
973+
974+
it("should not go into an infinite loop during claim refresh with adjusted clock skew", async function () {
975+
await toggleSignInSignUp(page);
976+
977+
// Override Date.now() to simulate a clock skew of 1 hour
978+
await page.evaluate(() => {
979+
globalThis.originalNow = Date.now;
980+
Date.now = function () {
981+
return originalNow() + 60 * 60 * 1000;
982+
};
983+
});
984+
985+
let claimRefreshCalledCount = 0;
986+
987+
await page.setRequestInterception(true);
988+
989+
page.on("request", (req) => {
990+
if (req.url() === `${TEST_APPLICATION_SERVER_BASE_URL}/auth/user/email/verify` && req.method() === "GET") {
991+
// Simulate a failure after 5 claim refresh API calls to avoid an infinite loop
992+
if (claimRefreshCalledCount >= 5) {
993+
return req.respond({ status: 500, contentType: "text/plain", body: "Something went wrong" });
994+
} else {
995+
claimRefreshCalledCount++;
996+
}
997+
}
998+
req.continue();
999+
});
1000+
1001+
const { fieldValues, postValues } = getDefaultSignUpFieldValues({ email: getTestEmail() });
1002+
await signUp(page, fieldValues, postValues, "emailpassword");
1003+
assert(claimRefreshCalledCount < 2);
1004+
});
1005+
});

test/unit/dateProvider.test.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* Copyright (c) 2022, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
import "@testing-library/jest-dom";
16+
import { BooleanClaim } from "../../recipe/session";
17+
import EmailPassword from "../../recipe/emailpassword";
18+
import SuperTokens from "../../lib/ts/superTokens";
19+
import assert from "assert";
20+
21+
describe("Date Provider test", function () {
22+
let storageLogs: string[] = [];
23+
24+
beforeEach(function () {
25+
SuperTokens.reset();
26+
storageLogs = [];
27+
});
28+
29+
it("should use custom date provider when calling init", async function () {
30+
SuperTokens.init({
31+
appInfo: {
32+
appName: "SuperTokens",
33+
websiteDomain: "supertokens.io",
34+
apiDomain: "api.supertokens.io",
35+
},
36+
dateProvider: function () {
37+
return {
38+
getClientClockSkewInMillis() {
39+
storageLogs.push("ST_LOGS DATE_PROVIDER_GET_CLIENT_CLOCK_SKEW");
40+
return 0;
41+
},
42+
setClientClockSkewInMillis() {
43+
storageLogs.push("ST_LOGS DATE_PROVIDER_SET_CLIENT_CLOCK_SKEW");
44+
},
45+
getThresholdInSeconds() {
46+
storageLogs.push("ST_LOGS DATE_PROVIDER_GET_THRESHOLD");
47+
return 7;
48+
},
49+
setThresholdInSeconds() {
50+
storageLogs.push("ST_LOGS DATE_PROVIDER_SET_THRESHOLD");
51+
return;
52+
},
53+
now() {
54+
storageLogs.push("ST_LOGS DATE_PROVIDER_NOW");
55+
return Date.now();
56+
},
57+
};
58+
},
59+
recipeList: [EmailPassword.init()],
60+
});
61+
62+
// Calling the shouldRefresh method on a SessionClaimValidator should call the DateProvider now method
63+
const claim = new BooleanClaim({ id: "test-claim", defaultMaxAgeInSeconds: 1000, refresh: async () => {} });
64+
const validator = claim.validators.isTrue();
65+
await validator.shouldRefresh({ "test-claim": { v: true, t: Date.now() } }, {});
66+
67+
assert.deepStrictEqual(storageLogs, ["ST_LOGS DATE_PROVIDER_GET_THRESHOLD", "ST_LOGS DATE_PROVIDER_NOW"]);
68+
});
69+
});

0 commit comments

Comments
 (0)