Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions lib/build/recipe/emailpassword/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined>;
Expand All @@ -53,6 +56,7 @@ export declare type TypeNormalisedInputResetPasswordUsingTokenFeature = {
};
export declare type TypeInput = {
signUpFeature?: TypeInputSignUp;
signInFeature?: TypeInputSignIn;
emailDelivery?: EmailDeliveryTypeInput<TypeEmailPasswordEmailDeliveryInput>;
override?: {
functions?: (
Expand Down
62 changes: 47 additions & 15 deletions lib/build/recipe/emailpassword/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand Down Expand Up @@ -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,
};
Expand Down
5 changes: 5 additions & 0 deletions lib/ts/recipe/emailpassword/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined>;
Expand All @@ -73,6 +77,7 @@ export type TypeNormalisedInputResetPasswordUsingTokenFeature = {

export type TypeInput = {
signUpFeature?: TypeInputSignUp;
signInFeature?: TypeInputSignIn;
emailDelivery?: EmailDeliveryTypeInput<TypeEmailPasswordEmailDeliveryInput>;
override?: {
functions?: (
Expand Down
64 changes: 51 additions & 13 deletions lib/ts/recipe/emailpassword/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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);

Expand Down Expand Up @@ -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[]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If done this way, it'd be a breaking change: if someone added an override for their email validators in the sign up config, they'd expect it to be applied here. I'd prefer if we kept it that way as well.

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "no password field give by user" contains a grammatical error. It should be "no password field given by user" to maintain proper grammar. This same typo appears in another comment about email fields as well. Consider correcting both instances for consistency.

Suggested change
// no password field give by user
// no password field given by user

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should fall back to the (normalized) email field validator for sign-up to avoid breaking apps.

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,
Expand Down
3 changes: 2 additions & 1 deletion lib/ts/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version number appears to be downgraded from 22.1.0 to 21.2.0, which is unexpected for a feature addition. This is likely an error in version management. Typically, version numbers should only increase when adding new features, following semantic versioning principles.

Suggested change
export const version = "21.2.0";
export const version = "22.2.0";

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.



export const cdiSupported = ["5.3"];

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
67 changes: 67 additions & 0 deletions test/emailpassword/signinFeature.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: "[email protected]",
},
{
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?");
});
});