Skip to content

Commit

Permalink
added basic tests and mock
Browse files Browse the repository at this point in the history
  • Loading branch information
niftyvictor committed Nov 25, 2024
1 parent d356701 commit e743046
Show file tree
Hide file tree
Showing 40 changed files with 2,317 additions and 339 deletions.
2 changes: 1 addition & 1 deletion lib/build/recipe/webauthn/api/emailExists.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down
83 changes: 46 additions & 37 deletions lib/build/recipe/webauthn/api/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ function getAPIImplementation() {
registerOptionsPOST: async function (_a) {
var { tenantId, options, userContext } = _a,
props = __rest(_a, ["tenantId", "options", "userContext"]);
const relyingPartyId = await options.config.relyingPartyId({
const relyingPartyId = await options.config.getRelyingPartyId({
tenantId,
request: options.req,
userContext,
});
const relyingPartyName = await options.config.relyingPartyName({
const relyingPartyName = await options.config.getRelyingPartyName({
tenantId,
userContext,
});
Expand Down Expand Up @@ -82,7 +82,7 @@ function getAPIImplementation() {
};
},
signInOptionsPOST: async function ({ email, tenantId, options, userContext }) {
const relyingPartyId = await options.config.relyingPartyId({
const relyingPartyId = await options.config.getRelyingPartyId({
tenantId,
request: options.req,
userContext,
Expand Down Expand Up @@ -124,13 +124,15 @@ function getAPIImplementation() {
options,
userContext,
}) {
// TODO update error codes (ERR_CODE_XXX) after final implementation
const errorCodeMap = {
SIGN_UP_NOT_ALLOWED:
"Cannot sign up due to security reasons. Please try logging in, use a different login method or contact support. (ERR_CODE_007)",
INVALID_AUTHENTICATOR_ERROR: {
// TODO: add more cases
},
WRONG_CREDENTIALS_ERROR: "The sign up credentials are incorrect. Please use a different authenticator.",
INVALID_CREDENTIALS_ERROR:
"The sign up credentials are incorrect. Please use a different authenticator.",
LINKING_TO_SESSION_USER_FAILED: {
EMAIL_VERIFICATION_REQUIRED:
"Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_013)",
Expand All @@ -148,17 +150,18 @@ function getAPIImplementation() {
userContext,
});
if (generatedOptions.status !== "OK") {
return { status: "WRONG_CREDENTIALS_ERROR" };
return generatedOptions;
}
const email = generatedOptions.email;
// NOTE: Following checks will likely never throw an error as the
// check for type is done in a parent function but they are kept
// here to be on the safe side.
if (!email) {
throw new Error(
"Should never come here since we already check that the email value is a string in validateFormFieldsOrThrowError"
"Should never come here since we already check that the email value is a string in validateEmailAddress"
);
}
// todo familiarize with this method
const preAuthCheckRes = await authUtils_1.AuthUtils.preAuthChecks({
authenticatingAccountInfo: {
recipeId: "webauthn",
Expand Down Expand Up @@ -228,6 +231,8 @@ function getAPIImplementation() {
"SIGN_UP_NOT_ALLOWED"
);
}
// todo familiarize with this method
// todo check if we need to remove webauthn credential ids from the type - it is not used atm.
const postAuthChecks = await authUtils_1.AuthUtils.postAuthChecks({
authenticatedUser: signUpResponse.user,
recipeUserId: signUpResponse.recipeUserId,
Expand Down Expand Up @@ -280,34 +285,38 @@ function getAPIImplementation() {
},
};
const recipeId = "webauthn";
// do the verification before in order to retrieve the user email
const verifyCredentialsResponse = await options.recipeImplementation.verifyCredentials({
const verifyResult = await options.recipeImplementation.verifyCredentials({
credential,
webauthnGeneratedOptionsId,
tenantId,
userContext,
});
const checkCredentialsOnTenant = async () => {
return verifyCredentialsResponse.status === "OK";
};
// doing it like this because the email is only available after verifyCredentials is called
let email;
if (verifyCredentialsResponse.status == "OK") {
const loginMethod = verifyCredentialsResponse.user.loginMethods.find((lm) => lm.recipeId === recipeId);
// there should be a webauthn login method and an email when trying to sign in using webauthn
if (!loginMethod || !loginMethod.email) {
return authUtils_1.AuthUtils.getErrorStatusResponseWithReason(
verifyCredentialsResponse,
errorCodeMap,
"SIGN_IN_NOT_ALLOWED"
);
}
email = loginMethod === null || loginMethod === void 0 ? void 0 : loginMethod.email;
} else {
if (verifyResult.status !== "OK") {
return verifyResult;
}
const generatedOptions = await options.recipeImplementation.getGeneratedOptions({
webauthnGeneratedOptionsId,
tenantId,
userContext,
});
if (generatedOptions.status !== "OK") {
return {
status: "WRONG_CREDENTIALS_ERROR",
status: "INVALID_CREDENTIALS_ERROR",
};
}
let email = generatedOptions.email;
const checkCredentialsOnTenant = async () => {
return true;
};
// todo familiarize with this method
// todo make sure the section below (from getAuthenticatingUserAndAddToCurrentTenantIfRequired to isVerified) is correct
// const matchingLoginMethodsFromSessionUser = sessionUser.loginMethods.filter(
// (lm) =>
// lm.recipeId === recipeId &&
// (lm.hasSameEmailAs(accountInfo.email) ||
// lm.hasSamePhoneNumberAs(accountInfo.phoneNumber) ||
// lm.hasSameThirdPartyInfoAs(accountInfo.thirdParty))
// );
const authenticatingUser = await authUtils_1.AuthUtils.getAuthenticatingUserAndAddToCurrentTenantIfRequired(
{
accountInfo: { email },
Expand All @@ -325,7 +334,7 @@ function getAPIImplementation() {
// isSignUpAllowed will be called as expected.
if (authenticatingUser === undefined) {
return {
status: "WRONG_CREDENTIALS_ERROR",
status: "INVALID_CREDENTIALS_ERROR",
};
}
const preAuthChecks = await authUtils_1.AuthUtils.preAuthChecks({
Expand Down Expand Up @@ -358,7 +367,7 @@ function getAPIImplementation() {
if (utils_1.isFakeEmail(email) && preAuthChecks.isFirstFactor) {
// Fake emails cannot be used as a first factor
return {
status: "WRONG_CREDENTIALS_ERROR",
status: "INVALID_CREDENTIALS_ERROR",
};
}
const signInResponse = await options.recipeImplementation.signIn({
Expand All @@ -369,7 +378,7 @@ function getAPIImplementation() {
tenantId,
userContext,
});
if (signInResponse.status === "WRONG_CREDENTIALS_ERROR") {
if (signInResponse.status === "INVALID_CREDENTIALS_ERROR") {
return signInResponse;
}
if (signInResponse.status !== "OK") {
Expand Down Expand Up @@ -690,17 +699,16 @@ function getAPIImplementation() {
credential,
userContext,
});
// todo decide how to handle these
if (updateResponse.status === "INVALID_AUTHENTICATOR_ERROR") {
// This should happen only cause of a race condition where the user
// might be deleted before token creation and consumption.
return {
status: "INVALID_AUTHENTICATOR_ERROR",
reason: updateResponse.reason,
};
} else if (updateResponse.status === "WRONG_CREDENTIALS_ERROR") {
} else if (updateResponse.status === "INVALID_CREDENTIALS_ERROR") {
return {
status: "WRONG_CREDENTIALS_ERROR",
status: "INVALID_CREDENTIALS_ERROR",
};
} else {
// status: "OK"
Expand Down Expand Up @@ -755,7 +763,6 @@ function getAPIImplementation() {
tenantId,
userContext,
});
// todo decide how to handle these
if (tokenConsumptionResponse.status === "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR") {
return tokenConsumptionResponse;
}
Expand Down Expand Up @@ -818,10 +825,12 @@ function getAPIImplementation() {
credential,
userContext,
});
// todo decide how to handle these
if (createUserResponse.status === "WRONG_CREDENTIALS_ERROR") {
return createUserResponse;
} else if (createUserResponse.status === "INVALID_AUTHENTICATOR_ERROR") {
if (
createUserResponse.status === "INVALID_CREDENTIALS_ERROR" ||
createUserResponse.status === "GENERATED_OPTIONS_NOT_FOUND_ERROR" ||
createUserResponse.status === "INVALID_GENERATED_OPTIONS_ERROR" ||
createUserResponse.status === "INVALID_AUTHENTICATOR_ERROR"
) {
return createUserResponse;
} else if (createUserResponse.status === "EMAIL_ALREADY_EXISTS_ERROR") {
// this means that the user already existed and we can just return an invalid
Expand Down
5 changes: 2 additions & 3 deletions lib/build/recipe/webauthn/api/recoverAccount.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand All @@ -23,12 +23,11 @@ const utils_1 = require("../../../utils");
const utils_2 = require("./utils");
const error_1 = __importDefault(require("../error"));
async function recoverAccount(apiImplementation, tenantId, options, userContext) {
// Logic as per https://github.com/supertokens/supertokens-node/issues/22#issuecomment-710512442
if (apiImplementation.recoverAccountPOST === undefined) {
return false;
}
const requestBody = await options.req.getJSONBody();
let webauthnGeneratedOptionsId = await utils_2.validatewebauthnGeneratedOptionsIdOrThrowError(
let webauthnGeneratedOptionsId = await utils_2.validateWebauthnGeneratedOptionsIdOrThrowError(
requestBody.webauthnGeneratedOptionsId
);
let credential = await utils_2.validateCredentialOrThrowError(requestBody.credential);
Expand Down
2 changes: 1 addition & 1 deletion lib/build/recipe/webauthn/api/registerOptions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down
2 changes: 1 addition & 1 deletion lib/build/recipe/webauthn/api/signInOptions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down
4 changes: 2 additions & 2 deletions lib/build/recipe/webauthn/api/signin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand All @@ -22,7 +22,7 @@ async function signInAPI(apiImplementation, tenantId, options, userContext) {
return false;
}
const requestBody = await options.req.getJSONBody();
const webauthnGeneratedOptionsId = await utils_2.validatewebauthnGeneratedOptionsIdOrThrowError(
const webauthnGeneratedOptionsId = await utils_2.validateWebauthnGeneratedOptionsIdOrThrowError(
requestBody.webauthnGeneratedOptionsId
);
const credential = await utils_2.validateCredentialOrThrowError(requestBody.credential);
Expand Down
14 changes: 4 additions & 10 deletions lib/build/recipe/webauthn/api/signup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down Expand Up @@ -28,7 +28,7 @@ async function signUpAPI(apiImplementation, tenantId, options, userContext) {
return false;
}
const requestBody = await options.req.getJSONBody();
const webauthnGeneratedOptionsId = await utils_2.validatewebauthnGeneratedOptionsIdOrThrowError(
const webauthnGeneratedOptionsId = await utils_2.validateWebauthnGeneratedOptionsIdOrThrowError(
requestBody.webauthnGeneratedOptionsId
);
const credential = await utils_2.validateCredentialOrThrowError(requestBody.credential);
Expand Down Expand Up @@ -63,14 +63,8 @@ async function signUpAPI(apiImplementation, tenantId, options, userContext) {
utils_1.send200Response(options.res, result);
} else if (result.status === "EMAIL_ALREADY_EXISTS_ERROR") {
throw new error_1.default({
type: error_1.default.FIELD_ERROR,
payload: [
{
id: "email",
error: "This email already exists. Please sign in instead.",
},
],
message: "Error in input formFields",
type: error_1.default.BAD_INPUT_ERROR,
message: "This email already exists. Please sign in instead.",
});
} else {
utils_1.send200Response(options.res, result);
Expand Down
2 changes: 1 addition & 1 deletion lib/build/recipe/webauthn/api/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-nocheck
export declare function validatewebauthnGeneratedOptionsIdOrThrowError(
export declare function validateWebauthnGeneratedOptionsIdOrThrowError(
webauthnGeneratedOptionsId: string
): Promise<string>;
export declare function validateCredentialOrThrowError<T>(credential: T): Promise<T>;
8 changes: 4 additions & 4 deletions lib/build/recipe/webauthn/api/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ var __importDefault =
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateCredentialOrThrowError = exports.validatewebauthnGeneratedOptionsIdOrThrowError = void 0;
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
exports.validateCredentialOrThrowError = exports.validateWebauthnGeneratedOptionsIdOrThrowError = void 0;
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand All @@ -21,13 +21,13 @@ exports.validateCredentialOrThrowError = exports.validatewebauthnGeneratedOption
* under the License.
*/
const error_1 = __importDefault(require("../error"));
async function validatewebauthnGeneratedOptionsIdOrThrowError(webauthnGeneratedOptionsId) {
async function validateWebauthnGeneratedOptionsIdOrThrowError(webauthnGeneratedOptionsId) {
if (webauthnGeneratedOptionsId === undefined) {
throw newBadRequestError("webauthnGeneratedOptionsId is required");
}
return webauthnGeneratedOptionsId;
}
exports.validatewebauthnGeneratedOptionsIdOrThrowError = validatewebauthnGeneratedOptionsIdOrThrowError;
exports.validateWebauthnGeneratedOptionsIdOrThrowError = validateWebauthnGeneratedOptionsIdOrThrowError;
async function validateCredentialOrThrowError(credential) {
if (credential === undefined) {
throw newBadRequestError("credential is required");
Expand Down
2 changes: 1 addition & 1 deletion lib/build/recipe/webauthn/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
Expand Down
Loading

0 comments on commit e743046

Please sign in to comment.