Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: example tests #799

Merged
merged 9 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Check our [guide](https://supertokens.com/docs/mfa/introduction) for more inform
- Removed an `ErrorBoundary` wrapping all our feature components to make sure all errors are properly catchable by the app
- In `supertokens-web-js` (which you may also be using), we added `firstFactors` into the return type of `getLoginMethods` and removed the enabled flags of different login methods.
- For older FDI versions, the firstFactors array will be calculated based on those enabled flags.
- Renamed `validatorId` in claim validation errors to `id` to match the backend SDKs

#### Migration guide

Expand Down Expand Up @@ -213,6 +214,32 @@ SuperTokens.init({
});
```

#### Renamed validatorId

If you used to use the `validatorId` prop of validationErrors, you should now use `id` instead.

Before:

```ts
async function checkValidators() {
const validationErrors = await Session.validateClaims();
for (const error of validationErrors) {
console.log(error.validatorId, error.reason);
}
}
```

After:

```ts
async function checkValidators() {
const validationErrors = await Session.validateClaims();
for (const error of validationErrors) {
console.log(error.id, error.reason);
}
}
```

### Changes

- Added support for FDI 1.19 (Node SDK>= 17.0.0), but keeping support FDI version 1.17 and 1.18 (node >= 15.0.0, golang>=0.13, python>=0.15.0)
Expand Down
10 changes: 9 additions & 1 deletion examples/with-multifactorauth-recovery-codes/backend/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,21 @@ export const SuperTokensConfig: TypeInput = {
const payload = input.session.getAccessTokenPayload();
const recoveryCodeHash = payload.recoveryCodeHash;
if (recoveryCodeHash) {
await UserMetadata.updateUserMetadata(input.session.getUserId(), {
const userId = input.session.getUserId();
await UserMetadata.updateUserMetadata(userId, {
recoveryCodeHash: null,
});
await input.session.setClaimValue(RecoveryCodeExistsClaim, false);
await input.session.mergeIntoAccessTokenPayload({
recoveryCodeHash: null,
});

const { devices } = await TOTP.listDevices(userId);
for (const dev of devices) {
if (dev.name !== input.deviceName) {
await TOTP.removeDevice(userId, dev.name);
}
}
}
}
return resp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function CreateRecoveryCode() {
<div onClick={createRecoveryCode} className="sessionButton createRecoveryCode">
Create recovery code
</div>
<NavLink to="/" className="sessionButton">
<NavLink to="/" className="sessionButton homeButton">
Go to Home page
</NavLink>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@
.separator-line {
max-width: 200px;
}

.app-container .main-container {
margin-block-end: 60px;
}
}

@media screen and (max-width: 480px) {
Expand All @@ -214,6 +218,6 @@
}

.app-container .main-container {
margin-block-end: 90px;
margin-block-end: 45px;
}
}
26 changes: 21 additions & 5 deletions examples/with-multifactorauth-recovery-codes/test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ describe("SuperTokens Example Basic tests", function () {
const recoveryCodeDiv = await page.waitForSelector(".recovery-code", { visible: true });
const recoveryCode = await recoveryCodeDiv.evaluate((e) => e.textContent);

// Log out
await page.click("div.bottom-links-container > div:nth-child(3) > div");
await page.click(".homeButton");
await page.waitForSelector("#user-id");

let logoutLink = await page.waitForSelector("div.bottom-links-container > div:nth-child(3) > div");
await logoutLink.click();

await setInputValues(page, [
{ name: "email", value: email },
{ name: "password", value: testPW },
Expand All @@ -140,24 +144,36 @@ describe("SuperTokens Example Basic tests", function () {
const newTOTPSecret = await getTOTPSecret(page);
await completeTOTP(page, newTOTPSecret);
await page.waitForSelector(".createRecoveryCode");
await page.click(".createRecoveryCode");
await page.waitForSelector(".recovery-code", { visible: true });
await page.click(".homeButton");
await page.waitForSelector("#user-id");

logoutLink = await page.waitForSelector("div.bottom-links-container > div:nth-child(3) > div");
await logoutLink.click();

await page.click("div.bottom-links-container > div:nth-child(3) > div");
await setInputValues(page, [
{ name: "email", value: email },
{ name: "password", value: testPW },
]);
await submitForm(page);
await completeTOTP(page, newTOTPSecret, 1);
await page.waitForSelector(".createRecoveryCode");

await page.waitForSelector("#user-id");
await page.click("div.bottom-links-container > div:nth-child(3) > div");

await setInputValues(page, [
{ name: "email", value: email },
{ name: "password", value: testPW },
]);
await submitForm(page);
await completeTOTP(page, origTOTPSecret, 1);
await page.waitForSelector(".createRecoveryCode");
const errorEle = await waitForSTElement(page, "[data-supertokens~=generalError");
const errorText = await errorEle.evaluate((e) => e.innerText);
assert.strictEqual(
errorText,
"Invalid TOTP. Please try again. 5 attempt(s) remaining before account is temporarily locked."
);
});
});
});
Expand Down
1 change: 1 addition & 0 deletions examples/with-phone-password-mfa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ This demo app uses the EmailPassword and Passwordless recipes to achieve the aut

- Change email validation logic on the backend (in emailpassword recipe) to validate phone number syntax.
- Change how password reset email is sent to instead send an SMS to the phone.
- We override consumeCodePOST to validate the phone number as if it was an email address to facilitate linking

## Future work:

Expand Down
36 changes: 34 additions & 2 deletions examples/with-phone-password-mfa/api-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ supertokens.init({
framework: "express",
supertokens: {
// TODO: This is a core hosted for demo purposes. You can use this, but make sure to change it to your core instance URI eventually.
connectionURI: "https://try.supertokens.com",
connectionURI: "http://localhost:3567",
apiKey: "<REQUIRED FOR MANAGED SERVICE, ELSE YOU CAN REMOVE THIS FIELD>",
},
appInfo: {
Expand Down Expand Up @@ -107,6 +107,38 @@ supertokens.init({
Passwordless.init({
contactMethod: "PHONE",
flowType: "USER_INPUT_CODE",
override: {
apis: (oI) => ({
...oI,
async consumeCodePOST(input) {
// Here we try to verify the phone number as an email address before
if (input.session !== undefined) {
if (!("userInputCode" in input)) {
throw new Error("We only enabled OTP");
}
const checkRes = await Passwordless.checkCode({
deviceId: input.deviceId,
userInputCode: input.userInputCode,
preAuthSessionId: input.preAuthSessionId,
tenantId: input.tenantId,
userContext: input.userContext,
});

if (checkRes.status === "OK" && checkRes.consumedDevice.phoneNumber !== undefined) {
const tokenRes = await EmailVerification.createEmailVerificationToken(
"public",
input.session.getRecipeUserId(),
checkRes.consumedDevice.phoneNumber
);
if (tokenRes.status === "OK") {
await EmailVerification.verifyEmailUsingToken("public", tokenRes.token, false);
}
}
}
return oI.consumeCodePOST!(input);
},
}),
},
}),
Session.init(),
MultiFactorAuth.init({
Expand Down Expand Up @@ -146,7 +178,7 @@ supertokens.init({
}),
}),
EmailVerification.init({
mode: "REQUIRED",
mode: "OPTIONAL",
}),
Dashboard.init(),
],
Expand Down
2 changes: 1 addition & 1 deletion examples/with-phone-password-mfa/test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe("SuperTokens Example Basic tests", function () {

browser = await puppeteer.launch({
args: ["--no-sandbox", "--disable-setuid-sandbox"],
headless: true,
headless: false,
});
page = await browser.newPage();
});
Expand Down
4 changes: 2 additions & 2 deletions lib/build/emailpassword-shared7.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/multifactorauth-shared.js

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

6 changes: 3 additions & 3 deletions lib/build/passwordless-shared3.js

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

2 changes: 2 additions & 0 deletions lib/build/recipe/totp/index.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/thirdparty-shared2.js

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

Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function useChildProps(
onFetchError: async (err: Response) => {
if (err.status === Session.getInstanceOrThrow().config.invalidClaimStatusCode) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerification.getInstanceOrThrow();
Expand Down Expand Up @@ -238,7 +238,7 @@ export function useChildProps(
onFetchError: async (err: Response) => {
if (err.status === Session.getInstanceOrThrow().config.invalidClaimStatusCode) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerification.getInstanceOrThrow();
Expand Down
4 changes: 2 additions & 2 deletions lib/ts/recipe/passwordless/components/features/mfa/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export function useChildProps(
onFetchError: async (err: Response) => {
if (err.status === Session.getInstanceOrThrow().config.invalidClaimStatusCode) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerification.getInstanceOrThrow();
Expand Down Expand Up @@ -362,7 +362,7 @@ function useOnLoad(
err.status === SessionRecipe.getInstanceOrThrow().config.invalidClaimStatusCode
) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerificationRecipe.getInstanceOrThrow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export function useChildProps(
onFetchError: async (err: Response) => {
if (err.status === Session.getInstanceOrThrow().config.invalidClaimStatusCode) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerification.getInstanceOrThrow();
Expand Down
2 changes: 1 addition & 1 deletion lib/ts/recipe/session/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const getFailureRedirectionInfo = async ({

let failedClaim: ClaimValidationError | undefined = undefined;
for (const validator of globalValidators) {
const claim = invalidClaims.find((c) => c.validatorId === validator.id);
const claim = invalidClaims.find((c) => c.id === validator.id);
if (claim !== undefined) {
const failureCallback = validator.onFailureRedirection;
if (failureCallback) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const SignInAndUpCallback: React.FC<PropType> = (props) => {
async (err: any) => {
if ("status" in err && err.status === Session.getInstanceOrThrow().config.invalidClaimStatusCode) {
const invalidClaims = await getInvalidClaimsFromResponse({ response: err, userContext });
if (invalidClaims.some((i) => i.validatorId === EmailVerificationClaim.id)) {
if (invalidClaims.some((i) => i.id === EmailVerificationClaim.id)) {
try {
// it's OK if this throws,
const evInstance = EmailVerification.getInstanceOrThrow();
Expand Down
Loading
Loading