Skip to content

Commit ab1f7fd

Browse files
authored
Merge pull request #1024 from supertokens/fix/fix-credential-listing-multiple-users
fix: WebAuthn credential listing/removal not working for non-primary WebAuthn users and multiple linked WebAuthn users
2 parents ef39958 + 7d36832 commit ab1f7fd

File tree

3 files changed

+98
-14
lines changed

3 files changed

+98
-14
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Updated FDI support to 4.2
1313
- Added `recipeUserId` in the WebAuthn list credentials response
14+
- Fix WebAuthn credential listing and removal to work even when the WebAuthn user is not the primary user and when there are multiple WebAuthn users linked
1415
- Prevent removal of WebAuthn credentials unless all session claims are satisfied
1516
- Change how sessions are fetched before listing, removing and adding WebAuthn credentials
1617

@@ -987,7 +988,7 @@ Session.init({
987988
input.userId,
988989
input.recipeUserId,
989990
input.tenantId,
990-
input.userContext,
991+
input.userContext
991992
)),
992993
};
993994

@@ -1015,7 +1016,7 @@ Session.init({
10151016
input.recipeUserId,
10161017
input.tenantId,
10171018
input.accessTokenPayload,
1018-
input.userContext,
1019+
input.userContext
10191020
)),
10201021
};
10211022

lib/build/recipe/webauthn/api/implementation.js

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

lib/ts/recipe/webauthn/api/implementation.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -986,14 +986,36 @@ export default function getAPIImplementation(): APIInterface {
986986
},
987987

988988
listCredentialsGET: async function ({ options, userContext, session }) {
989-
const credentials = await options.recipeImplementation.listCredentials({
990-
recipeUserId: session.getRecipeUserId().getAsString(),
991-
userContext,
992-
});
989+
const existingUser = await getUser(session.getUserId(), userContext);
990+
if (!existingUser) {
991+
return {
992+
status: "GENERAL_ERROR",
993+
message: "User not found",
994+
};
995+
}
996+
997+
const recipeUserIds = existingUser.loginMethods
998+
.filter((lm) => lm.recipeId === "webauthn")
999+
?.map((lm) => lm.recipeUserId);
1000+
1001+
const credentials: {
1002+
webauthnCredentialId: string;
1003+
relyingPartyId: string;
1004+
createdAt: number;
1005+
recipeUserId: string;
1006+
}[] = [];
1007+
for (const recipeUserId of recipeUserIds) {
1008+
const listCredentialsResponse = await options.recipeImplementation.listCredentials({
1009+
recipeUserId: recipeUserId.getAsString(),
1010+
userContext,
1011+
});
1012+
1013+
credentials.push(...listCredentialsResponse.credentials);
1014+
}
9931015

9941016
return {
9951017
status: "OK",
996-
credentials: credentials.credentials.map((credential) => ({
1018+
credentials: credentials.map((credential) => ({
9971019
recipeUserId: credential.recipeUserId,
9981020
webauthnCredentialId: credential.webauthnCredentialId,
9991021
relyingPartyId: credential.relyingPartyId,
@@ -1076,9 +1098,28 @@ export default function getAPIImplementation(): APIInterface {
10761098
]);
10771099
}
10781100

1101+
const user = await getUser(session.getUserId(), userContext);
1102+
if (!user) {
1103+
return {
1104+
status: "GENERAL_ERROR",
1105+
message: "User not found",
1106+
};
1107+
}
1108+
1109+
const recipeUserId = user.loginMethods.find(
1110+
(lm) => lm.recipeId === "webauthn" && lm.webauthn?.credentialIds.includes(webauthnCredentialId)
1111+
)?.recipeUserId;
1112+
1113+
if (!recipeUserId) {
1114+
return {
1115+
status: "GENERAL_ERROR",
1116+
message: "User not found",
1117+
};
1118+
}
1119+
10791120
const removeCredentialResponse = await options.recipeImplementation.removeCredential({
10801121
webauthnCredentialId,
1081-
recipeUserId: session.getRecipeUserId().getAsString(),
1122+
recipeUserId: recipeUserId.getAsString(),
10821123
userContext,
10831124
});
10841125
if (removeCredentialResponse.status !== "OK") {

0 commit comments

Comments
 (0)