|
| 1 | +/* |
| 2 | + * Copyright (c) 2022, SuperTokens.com |
| 3 | + * All rights reserved. |
| 4 | + */ |
| 5 | + |
| 6 | +import fetch from "isomorphic-fetch"; |
| 7 | +import { TEST_SERVER_BASE_URL, TEST_CLIENT_BASE_URL } from "../constants"; |
| 8 | +import { |
| 9 | + backendBeforeEach, |
| 10 | + setupBrowser, |
| 11 | + screenshotOnFailure, |
| 12 | + clearBrowserCookiesWithoutAffectingConsole, |
| 13 | + toggleSignInSignUp, |
| 14 | + getTestEmail, |
| 15 | + waitForSTElement, |
| 16 | + submitForm, |
| 17 | + setEnabledRecipes, |
| 18 | + setInputValues, |
| 19 | + setAccountLinkingConfig, |
| 20 | + getPasswordlessDevice, |
| 21 | +} from "../helpers"; |
| 22 | +import { tryWebauthnSignUp } from "./webauthn.helpers"; |
| 23 | +import assert from "assert"; |
| 24 | + |
| 25 | +/* |
| 26 | + * Test case: |
| 27 | + * 1. The app has account linking disabled |
| 28 | + * 2. A user signs up using a non-webauthn factor (e.g.: passwordless) |
| 29 | + * 3. The user now tries signing up with webauthn using the same email |
| 30 | + * -> this should work and create an entirely separate user with an unverified email address |
| 31 | + */ |
| 32 | +describe("SuperTokens WebAuthn Account Linking", function () { |
| 33 | + let browser; |
| 34 | + let page; |
| 35 | + let consoleLogs = []; |
| 36 | + let userId1; |
| 37 | + let userId2; |
| 38 | + const email = getTestEmail(); |
| 39 | + |
| 40 | + before(async function () { |
| 41 | + await backendBeforeEach(); |
| 42 | + |
| 43 | + await fetch(`${TEST_SERVER_BASE_URL}/startst`, { |
| 44 | + method: "POST", |
| 45 | + }).catch(console.error); |
| 46 | + |
| 47 | + await setEnabledRecipes([ |
| 48 | + "webauthn", |
| 49 | + "emailpassword", |
| 50 | + "passwordless", |
| 51 | + "session", |
| 52 | + "dashboard", |
| 53 | + "userroles", |
| 54 | + "multifactorauth", |
| 55 | + ]); |
| 56 | + |
| 57 | + browser = await setupBrowser(); |
| 58 | + page = await browser.newPage(); |
| 59 | + page.on("console", (consoleObj) => { |
| 60 | + const log = consoleObj.text(); |
| 61 | + if (log.startsWith("ST_LOGS")) { |
| 62 | + consoleLogs.push(log); |
| 63 | + } |
| 64 | + }); |
| 65 | + }); |
| 66 | + |
| 67 | + after(async function () { |
| 68 | + await browser.close(); |
| 69 | + await fetch(`${TEST_SERVER_BASE_URL}/after`, { |
| 70 | + method: "POST", |
| 71 | + }).catch(console.error); |
| 72 | + |
| 73 | + await fetch(`${TEST_SERVER_BASE_URL}/stopst`, { |
| 74 | + method: "POST", |
| 75 | + }).catch(console.error); |
| 76 | + }); |
| 77 | + |
| 78 | + afterEach(function () { |
| 79 | + return screenshotOnFailure(this, browser); |
| 80 | + }); |
| 81 | + |
| 82 | + beforeEach(async function () { |
| 83 | + consoleLogs = []; |
| 84 | + consoleLogs = await clearBrowserCookiesWithoutAffectingConsole(page, consoleLogs); |
| 85 | + await toggleSignInSignUp(page); |
| 86 | + }); |
| 87 | + |
| 88 | + it("Should create separate users when signing up with same email using different auth methods (account linking disabled)", async function () { |
| 89 | + // Disable account linking |
| 90 | + await setAccountLinkingConfig(false, false); |
| 91 | + const email = await getTestEmail(); |
| 92 | + |
| 93 | + await Promise.all([ |
| 94 | + page.goto(`${TEST_CLIENT_BASE_URL}/auth?authRecipe=passwordless`), |
| 95 | + page.waitForNavigation({ waitUntil: "networkidle0" }), |
| 96 | + ]); |
| 97 | + |
| 98 | + // Signup using the email |
| 99 | + await setInputValues(page, [{ name: "email", value: email }]); |
| 100 | + await submitForm(page); |
| 101 | + |
| 102 | + await waitForSTElement(page, "[data-supertokens~=input][name=userInputCode]"); |
| 103 | + |
| 104 | + const loginAttemptInfo = JSON.parse( |
| 105 | + await page.evaluate(() => localStorage.getItem("supertokens-passwordless-loginAttemptInfo")) |
| 106 | + ); |
| 107 | + const device = await getPasswordlessDevice(loginAttemptInfo); |
| 108 | + await setInputValues(page, [{ name: "userInputCode", value: device.codes[0].userInputCode }]); |
| 109 | + await submitForm(page); |
| 110 | + await page.waitForTimeout(2000); |
| 111 | + |
| 112 | + // We want to parse the text inside the session-context-userId div |
| 113 | + const userId1 = await page.evaluate(() => document.querySelector(".session-context-userId").textContent); |
| 114 | + |
| 115 | + // Find the div with classname logoutButton and click it using normal |
| 116 | + // puppeteer selector |
| 117 | + const logoutButton = await page.waitForSelector("div.logoutButton"); |
| 118 | + await logoutButton.click(); |
| 119 | + await new Promise((res) => setTimeout(res, 1000)); |
| 120 | + |
| 121 | + await tryWebauthnSignUp(page, email); |
| 122 | + |
| 123 | + // We should be in the confirmation page now. |
| 124 | + await submitForm(page); |
| 125 | + await page.waitForTimeout(1000); |
| 126 | + |
| 127 | + // Extract second userId from console logs |
| 128 | + const userId2 = await page.evaluate(() => document.querySelector(".session-context-userId").textContent); |
| 129 | + |
| 130 | + // Verify that two different users were created |
| 131 | + assert.notStrictEqual( |
| 132 | + userId1, |
| 133 | + userId2, |
| 134 | + "Different auth methods with same email should create separate users when account linking is disabled" |
| 135 | + ); |
| 136 | + }); |
| 137 | +}); |
0 commit comments