Skip to content

Commit 2ef4cb9

Browse files
Add third flow for testing webauthn signup on accountlinking disabled
1 parent 1fa2cb1 commit 2ef4cb9

File tree

3 files changed

+141
-16
lines changed

3 files changed

+141
-16
lines changed

lib/build/recipe/webauthn/index.d.ts

Lines changed: 2 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ts/recipe/webauthn/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,7 @@ export default class Wrapper {
247247
return Webauthn.getInstanceOrThrow().webJSRecipe.recoverAccount(input);
248248
}
249249

250-
static registerCredential(input: {
251-
registrationOptions: Omit<RegistrationOptions, "fetchResponse" | "status">;
252-
userContext: any;
253-
}): Promise<
250+
static registerCredential(input: { registrationOptions: RegistrationOptions; userContext: any }): Promise<
254251
| {
255252
status: "OK";
256253
registrationResponse: RegistrationResponseJSON;
@@ -270,10 +267,7 @@ export default class Wrapper {
270267
return Webauthn.getInstanceOrThrow().webJSRecipe.registerCredential(input);
271268
}
272269

273-
static authenticateCredential(input: {
274-
authenticationOptions: Omit<AuthenticationOptions, "fetchResponse" | "status">;
275-
userContext: any;
276-
}): Promise<
270+
static authenticateCredential(input: { authenticationOptions: AuthenticationOptions; userContext: any }): Promise<
277271
| {
278272
status: "OK";
279273
authenticationResponse: AuthenticationResponseJSON;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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

Comments
 (0)