diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b60e1146..c624e5905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Return type of `authGET` in `APIInterface` in `oauth2provider` recipe - Return type of `loginGET` in `APIInterface` in `oauth2provider` recipe +## [21.2.0] - 2024-11-19 + +- Adds signInFeature type and validation for form fields in EmailPassword Recipe. + ## [21.1.0] - 2024-11-19 - Adds `getCookieNameForTokenType` config option to allow customizing the cookie name for a token type. diff --git a/lib/build/recipe/emailpassword/types.d.ts b/lib/build/recipe/emailpassword/types.d.ts index fa3eb1408..cbf775a02 100644 --- a/lib/build/recipe/emailpassword/types.d.ts +++ b/lib/build/recipe/emailpassword/types.d.ts @@ -36,6 +36,9 @@ export declare type TypeFormField = { export declare type TypeInputSignUp = { formFields?: TypeInputFormField[]; }; +export declare type TypeInputSignIn = { + formFields?: TypeInputFormField[]; +}; export declare type NormalisedFormField = { id: string; validate: (value: any, tenantId: string, userContext: UserContext) => Promise; @@ -53,6 +56,7 @@ export declare type TypeNormalisedInputResetPasswordUsingTokenFeature = { }; export declare type TypeInput = { signUpFeature?: TypeInputSignUp; + signInFeature?: TypeInputSignIn; emailDelivery?: EmailDeliveryTypeInput; override?: { functions?: ( diff --git a/lib/build/recipe/emailpassword/utils.js b/lib/build/recipe/emailpassword/utils.js index 18014af69..9950e3810 100644 --- a/lib/build/recipe/emailpassword/utils.js +++ b/lib/build/recipe/emailpassword/utils.js @@ -28,7 +28,11 @@ function validateAndNormaliseUserInput(recipeInstance, appInfo, config) { appInfo, config === undefined ? undefined : config.signUpFeature ); - let signInFeature = validateAndNormaliseSignInConfig(recipeInstance, appInfo, signUpFeature); + let signInFeature = validateAndNormaliseSignInConfig( + recipeInstance, + appInfo, + config === undefined ? undefined : config.signInFeature + ); let resetPasswordUsingTokenFeature = validateAndNormaliseResetPasswordUsingTokenConfig(signUpFeature); let override = Object.assign( { @@ -99,22 +103,50 @@ function validateAndNormaliseResetPasswordUsingTokenConfig(signUpConfig) { }; } function normaliseSignInFormFields(formFields) { - return formFields - .filter( - (filter) => - filter.id === constants_1.FORM_FIELD_EMAIL_ID || filter.id === constants_1.FORM_FIELD_PASSWORD_ID - ) - .map((field) => { - return { - id: field.id, - // see issue: https://github.com/supertokens/supertokens-node/issues/36 - validate: field.id === constants_1.FORM_FIELD_EMAIL_ID ? field.validate : defaultValidator, - optional: false, - }; + let normalisedFormFields = []; + if (formFields !== undefined) { + formFields.forEach((field) => { + if (field.id === constants_1.FORM_FIELD_PASSWORD_ID) { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultPasswordValidator : field.validate, + optional: false, + }); + } else if (field.id === constants_1.FORM_FIELD_EMAIL_ID) { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultEmailValidator : field.validate, + optional: false, + }); + } else { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultValidator : field.validate, + optional: field.optional === undefined ? false : field.optional, + }); + } + }); + } + if (normalisedFormFields.filter((field) => field.id === constants_1.FORM_FIELD_PASSWORD_ID).length === 0) { + // no password field give by user + normalisedFormFields.push({ + id: constants_1.FORM_FIELD_PASSWORD_ID, + validate: defaultPasswordValidator, + optional: false, + }); + } + if (normalisedFormFields.filter((field) => field.id === constants_1.FORM_FIELD_EMAIL_ID).length === 0) { + // no email field give by user + normalisedFormFields.push({ + id: constants_1.FORM_FIELD_EMAIL_ID, + validate: defaultEmailValidator, + optional: false, }); + } + return normalisedFormFields; } -function validateAndNormaliseSignInConfig(_, __, signUpConfig) { - let formFields = normaliseSignInFormFields(signUpConfig.formFields); +function validateAndNormaliseSignInConfig(_, __, config) { + let formFields = normaliseSignInFormFields(config === undefined ? undefined : config.formFields); return { formFields, }; diff --git a/lib/ts/recipe/emailpassword/types.ts b/lib/ts/recipe/emailpassword/types.ts index 0fdfbe088..a1e650bad 100644 --- a/lib/ts/recipe/emailpassword/types.ts +++ b/lib/ts/recipe/emailpassword/types.ts @@ -52,6 +52,10 @@ export type TypeInputSignUp = { formFields?: TypeInputFormField[]; }; +export type TypeInputSignIn = { + formFields?: TypeInputFormField[]; +}; + export type NormalisedFormField = { id: string; validate: (value: any, tenantId: string, userContext: UserContext) => Promise; @@ -73,6 +77,7 @@ export type TypeNormalisedInputResetPasswordUsingTokenFeature = { export type TypeInput = { signUpFeature?: TypeInputSignUp; + signInFeature?: TypeInputSignIn; emailDelivery?: EmailDeliveryTypeInput; override?: { functions?: ( diff --git a/lib/ts/recipe/emailpassword/utils.ts b/lib/ts/recipe/emailpassword/utils.ts index f6ce4316d..2d0f4ec73 100644 --- a/lib/ts/recipe/emailpassword/utils.ts +++ b/lib/ts/recipe/emailpassword/utils.ts @@ -23,6 +23,7 @@ import { TypeNormalisedInputResetPasswordUsingTokenFeature, NormalisedFormField, TypeInputFormField, + TypeInputSignIn, } from "./types"; import { NormalisedAppinfo, UserContext } from "../../types"; import { FORM_FIELD_EMAIL_ID, FORM_FIELD_PASSWORD_ID } from "./constants"; @@ -41,7 +42,11 @@ export function validateAndNormaliseUserInput( config === undefined ? undefined : config.signUpFeature ); - let signInFeature = validateAndNormaliseSignInConfig(recipeInstance, appInfo, signUpFeature); + let signInFeature = validateAndNormaliseSignInConfig( + recipeInstance, + appInfo, + config === undefined ? undefined : config.signInFeature + ); let resetPasswordUsingTokenFeature = validateAndNormaliseResetPasswordUsingTokenConfig(signUpFeature); @@ -114,25 +119,58 @@ function validateAndNormaliseResetPasswordUsingTokenConfig( }; } -function normaliseSignInFormFields(formFields: NormalisedFormField[]) { - return formFields - .filter((filter) => filter.id === FORM_FIELD_EMAIL_ID || filter.id === FORM_FIELD_PASSWORD_ID) - .map((field) => { - return { - id: field.id, - // see issue: https://github.com/supertokens/supertokens-node/issues/36 - validate: field.id === FORM_FIELD_EMAIL_ID ? field.validate : defaultValidator, - optional: false, - }; +function normaliseSignInFormFields(formFields?: TypeInputFormField[]) { + let normalisedFormFields: NormalisedFormField[] = []; + if (formFields !== undefined) { + formFields.forEach((field) => { + if (field.id === FORM_FIELD_PASSWORD_ID) { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultValidator : field.validate, + optional: false, + }); + } else if (field.id === FORM_FIELD_EMAIL_ID) { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultEmailValidator : field.validate, + optional: false, + }); + } else { + normalisedFormFields.push({ + id: field.id, + validate: field.validate === undefined ? defaultValidator : field.validate, + optional: field.optional === undefined ? false : field.optional, + }); + } + }); + } + if (!normalisedFormFields.some((field) => field.id === FORM_FIELD_PASSWORD_ID)) { + // no password field give by user + normalisedFormFields.push({ + id: FORM_FIELD_PASSWORD_ID, + validate: defaultValidator, + optional: false, }); + } + if (!normalisedFormFields.some((field) => field.id === FORM_FIELD_EMAIL_ID)) { + // no email field give by user + normalisedFormFields.push({ + id: FORM_FIELD_EMAIL_ID, + validate: defaultEmailValidator, + optional: false, + }); + } + return normalisedFormFields; } function validateAndNormaliseSignInConfig( _: Recipe, __: NormalisedAppinfo, - signUpConfig: TypeNormalisedInputSignUp + config?: TypeInputSignIn ): TypeNormalisedInputSignIn { - let formFields: NormalisedFormField[] = normaliseSignInFormFields(signUpConfig.formFields); + let formFields: NormalisedFormField[] = normaliseSignInFormFields( + config === undefined ? undefined : config.formFields + ); return { formFields, diff --git a/lib/ts/version.ts b/lib/ts/version.ts index 46b29d786..3e75afca8 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,7 +12,8 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const version = "22.1.0"; +export const version = "21.2.0"; + export const cdiSupported = ["5.3"]; diff --git a/package-lock.json b/package-lock.json index b9c026dd8..e80b6932e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-node", - "version": "22.1.0", + "version": "21.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "supertokens-node", - "version": "22.1.0", + "version": "21.2.0", "license": "Apache-2.0", "dependencies": { "buffer": "^6.0.3", diff --git a/package.json b/package.json index 1bc5a0a41..735a154d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-node", - "version": "22.1.0", + "version": "21.2.0", "description": "NodeJS driver for SuperTokens core", "main": "index.js", "scripts": { diff --git a/test/emailpassword/signinFeature.test.js b/test/emailpassword/signinFeature.test.js index 15f95f7d6..780c5bf6d 100644 --- a/test/emailpassword/signinFeature.test.js +++ b/test/emailpassword/signinFeature.test.js @@ -1887,4 +1887,71 @@ describe(`signinFeature: ${printPath("[test/emailpassword/signinFeature.test.js] ); assert(invalidEmailResponse.status === "WRONG_CREDENTIALS_ERROR"); }); + + // test case where more than the configured form fields are passed. + it("test bad case when too many formFields are passed", async function () { + const connectionURI = await startST(); + STExpress.init({ + supertokens: { + connectionURI, + }, + appInfo: { + apiDomain: "api.supertokens.io", + appName: "SuperTokens", + websiteDomain: "supertokens.io", + }, + recipeList: [ + EmailPassword.init({ + signInFeature: { + formFields: [ + { + id: "testField", + }, + ], + }, + }), + Session.init({ getTokenTransferMethod: () => "cookie" }), + ], + }); + const app = express(); + + app.use(middleware()); + + app.use(errorHandler()); + + let response = await new Promise((resolve) => + request(app) + .post("/auth/signin") + .send({ + formFields: [ + { + id: "password", + value: "validpass123", + }, + { + id: "email", + value: "random@gmail.com", + }, + { + id: "testField", + value: "some value", + }, + { + id: "extraField", + value: "some value", + }, + ], + }) + .expect(400) + .end((err, res) => { + if (err) { + resolve(undefined); + } else { + resolve(JSON.parse(res.text)); + } + }) + ); + + assert(response.message == "Are you sending too many formFields?"); + }); });