Skip to content

Commit cd5ddc1

Browse files
feat: add integration tests for webauthn
1 parent 8e3c08d commit cd5ddc1

File tree

4 files changed

+201
-9
lines changed

4 files changed

+201
-9
lines changed

test/integration/emailverification.test.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ describe("EmailVerification Integration Tests", function () {
3232
enabledRecipes: ["emailpassword", "emailverification", "session"],
3333
});
3434

35-
// Set up proper cookie handling for Node.js environment
36-
const cookieStore = new Map();
37-
3835
SuperTokens.init({
3936
appInfo: {
4037
appName: "SuperTokens",

test/integration/webauthn.test.js

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/* Copyright (c) 2021, 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+
import assert from "assert";
17+
import jsdom from "mocha-jsdom";
18+
import "isomorphic-fetch";
19+
import SuperTokens from "../../lib/build/supertokens";
20+
import Webauthn from "../../lib/build/recipe/webauthn/index.js";
21+
import Session from "../../lib/build/recipe/session/index.js";
22+
import { getTestEmail, setupCoreApp, setupST, backendBeforeEach } from "../helpers.js";
23+
import { TEST_SERVER_BASE_URL } from "../constants.js";
24+
25+
describe("Webauthn Integration Tests", function () {
26+
jsdom({ url: "http://localhost.org" });
27+
28+
before(async function () {
29+
const coreUrl = await setupCoreApp();
30+
await setupST({
31+
coreUrl,
32+
enabledRecipes: ["webauthn", "session"],
33+
});
34+
35+
SuperTokens.init({
36+
appInfo: {
37+
appName: "SuperTokens",
38+
apiDomain: "http://127.0.0.1:8082",
39+
apiBasePath: "/auth",
40+
},
41+
recipeList: [Webauthn.init(), Session.init({ tokenTransferMethod: "header" })],
42+
});
43+
});
44+
45+
beforeEach(async function () {
46+
await backendBeforeEach();
47+
});
48+
49+
describe("Webauthn SignUp Flow", function () {
50+
it("should successfully get register options for email", async function () {
51+
const testEmail = getTestEmail();
52+
53+
const response = await Webauthn.getRegisterOptions({
54+
email: testEmail,
55+
userContext: {},
56+
});
57+
58+
assert.strictEqual(response.status, "OK");
59+
assert.ok(response.webauthnGeneratedOptionsId);
60+
assert.strictEqual(response.rp.name, "SuperTokens");
61+
});
62+
63+
it("should handle registration flow with mocked credentials", async function () {
64+
const testEmail = getTestEmail();
65+
66+
// Get registration options
67+
const registerOptionsResponse = await Webauthn.getRegisterOptions({
68+
email: testEmail,
69+
});
70+
71+
assert.strictEqual(registerOptionsResponse.status, "OK");
72+
73+
// Create mocked credential using the test server endpoint
74+
const mockCredentialResponse = await fetch(`${TEST_SERVER_BASE_URL}/test/webauthn/create-credential`, {
75+
method: "POST",
76+
headers: {
77+
"Content-Type": "application/json",
78+
},
79+
body: JSON.stringify({
80+
registerOptionsResponse: registerOptionsResponse,
81+
rpId: "localhost",
82+
rpName: "SuperTokens",
83+
origin: "http://localhost:3031",
84+
}),
85+
});
86+
87+
const mockCredential = await mockCredentialResponse.json();
88+
assert.ok(mockCredential.credential);
89+
90+
// Perform sign up with the mocked credential
91+
const signUpResponse = await Webauthn.signUp({
92+
webauthnGeneratedOptionsId: registerOptionsResponse.webauthnGeneratedOptionsId,
93+
credential: mockCredential.credential,
94+
});
95+
96+
assert.strictEqual(signUpResponse.status, "OK");
97+
assert.ok(signUpResponse.user);
98+
assert.ok(signUpResponse.user.id);
99+
assert.ok(signUpResponse.user.emails.includes(testEmail));
100+
});
101+
});
102+
103+
describe("Webauthn SignIn Flow", function () {
104+
const testEmail = getTestEmail();
105+
let signInOptionsResponse;
106+
107+
it("should successfully get sign in options", async function () {
108+
const response = await Webauthn.getSignInOptions();
109+
110+
assert.strictEqual(response.status, "OK");
111+
assert.ok(response.webauthnGeneratedOptionsId);
112+
assert.ok(response.challenge);
113+
114+
signInOptionsResponse = response;
115+
});
116+
117+
it("should successfully sign in after authenticating credential", async function () {
118+
// Simulate the authenticate credential flow using server
119+
const registrationOptions = await Webauthn.getRegisterOptions({
120+
email: testEmail,
121+
});
122+
123+
const response = await fetch(`${TEST_SERVER_BASE_URL}/test/webauthn/create-and-assert-credential`, {
124+
method: "POST",
125+
body: JSON.stringify({
126+
registerOptionsResponse: registrationOptions,
127+
signInOptionsResponse: signInOptionsResponse,
128+
rpId: "localhost",
129+
rpName: "SuperTokens",
130+
origin: "http://localhost:3031",
131+
}),
132+
headers: {
133+
"Content-Type": "application/json",
134+
},
135+
});
136+
137+
assert(response.status === 200);
138+
139+
const responseJson = await response.json();
140+
141+
const { attestation, assertion } = responseJson.credential;
142+
143+
assert.ok(attestation);
144+
assert.ok(assertion);
145+
146+
const signUpResponse = await Webauthn.signUp({
147+
webauthnGeneratedOptionsId: registrationOptions.webauthnGeneratedOptionsId,
148+
credential: attestation,
149+
});
150+
151+
assert.strictEqual(signUpResponse.status, "OK");
152+
assert.ok(signUpResponse.user);
153+
assert.ok(signUpResponse.user.id);
154+
155+
// Simulation ends here, now we need to sign in the user
156+
157+
const signInResponse = await Webauthn.signIn({
158+
webauthnGeneratedOptionsId: signInOptionsResponse.webauthnGeneratedOptionsId,
159+
credential: assertion,
160+
userContext: {},
161+
});
162+
163+
assert.strictEqual(signInResponse.status, "OK");
164+
assert.ok(signInResponse.user);
165+
assert.ok(signInResponse.user.id);
166+
assert.ok(signInResponse.user.emails.includes(testEmail));
167+
});
168+
});
169+
170+
describe("Webauthn Utility Methods", function () {
171+
it("should handle webauthn URL extraction methods", async function () {
172+
try {
173+
// These methods should not throw errors even without webauthn context
174+
const deviceId = Webauthn.getDeviceIdFromURL();
175+
assert.ok(typeof deviceId === "string");
176+
} catch (error) {
177+
// It's acceptable if these throw in a non-webauthn context
178+
assert.ok(error);
179+
}
180+
});
181+
182+
it("should handle credential creation errors gracefully", async function () {
183+
try {
184+
// This should fail with invalid options
185+
await Webauthn.signUp({
186+
webauthnGeneratedOptionsId: "invalid-id",
187+
credential: {},
188+
userContext: {},
189+
});
190+
assert.fail("Should have thrown error with invalid options");
191+
} catch (error) {
192+
assert.ok(error);
193+
}
194+
});
195+
});
196+
});

test/server/package-lock.json

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

test/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"isomorphic-fetch": "^3.0.0",
1818
"morgan": "^1.10.0",
1919
"otpauth": "^9.2.0",
20-
"supertokens-node": "^22.0.0"
20+
"supertokens-node": "^23.0.0"
2121
}
2222
}

0 commit comments

Comments
 (0)