From 43305a562cfba914654da056b0a7e03c40849678 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Fri, 2 Jan 2026 13:41:26 +0100 Subject: [PATCH] feat(medusa,utils,core-flows): add reset password metdata (#14417) ## Summary **What** Adds metadata field for reset password route that allows passing data from caller that can be found in the subscriber. ## Checklist Please ensure the following before requesting a review: - [x] I have added a **changeset** for this PR - Every non-breaking change should be marked as a **patch** - To add a changeset, run `yarn changeset` and follow the prompts - [x] The changes are covered by relevant **tests** - [x] I have verified the code works as intended locally - [ ] I have linked the related issue(s) if applicable --- > [!NOTE] > Introduces optional request `metadata` for reset-password and propagates it through to event subscribers. > > - Accepts `metadata` in `ResetPasswordRequest` validator and `reset-password` route; forwards it to `generateResetPasswordTokenWorkflow` > - Workflow now accepts `metadata` and includes it in emitted `auth.password_reset` event data > - Updates event docs to mention `metadata` field > - Adds integration test verifying `metadata` is emitted in the password reset event > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7f9855feabed284336e8872eebfb18fe3bd320db. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .changeset/seven-lions-cheat.md | 7 ++++ .../http/__tests__/auth/admin/auth.spec.ts | 41 +++++++++++++++++++ .../generate-reset-password-token.ts | 4 +- packages/core/utils/src/core-flows/events.ts | 7 ++-- .../[auth_provider]/reset-password/route.ts | 3 +- packages/medusa/src/api/auth/validators.ts | 1 + 6 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 .changeset/seven-lions-cheat.md diff --git a/.changeset/seven-lions-cheat.md b/.changeset/seven-lions-cheat.md new file mode 100644 index 0000000000..6fd781275a --- /dev/null +++ b/.changeset/seven-lions-cheat.md @@ -0,0 +1,7 @@ +--- +"@medusajs/core-flows": patch +"@medusajs/utils": patch +"@medusajs/medusa": patch +--- + +feat(medusa,utils,core-flows): add reset password metdata diff --git a/integration-tests/http/__tests__/auth/admin/auth.spec.ts b/integration-tests/http/__tests__/auth/admin/auth.spec.ts index 4281a1a4cb..f9510855a4 100644 --- a/integration-tests/http/__tests__/auth/admin/auth.spec.ts +++ b/integration-tests/http/__tests__/auth/admin/auth.spec.ts @@ -1,4 +1,5 @@ import { generateResetPasswordTokenWorkflow } from "@medusajs/core-flows" +import { AuthWorkflowEvents, Modules } from "@medusajs/framework/utils" import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import jwt from "jsonwebtoken" import { @@ -431,6 +432,46 @@ medusaIntegrationTestRunner({ expect(response.response.status).toEqual(401) expect(response.response.data.message).toEqual("Invalid token") }) + + it("should emit metadata in password reset event", async () => { + await api.post("/auth/user/emailpass/register", { + email: "test-metadata@medusa-commerce.com", + password: "secret_password", + }) + + const eventBus = container.resolve(Modules.EVENT_BUS) + const subscriber = jest.fn() + + eventBus.subscribe(AuthWorkflowEvents.PASSWORD_RESET, subscriber) + + const metadata = { + source: "test", + userId: "123", + customField: "customValue", + } + + const response = await api.post("/auth/user/emailpass/reset-password", { + identifier: "test-metadata@medusa-commerce.com", + metadata: metadata, + }) + + expect(response.status).toEqual(201) + + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(subscriber).toHaveBeenCalledTimes(1) + + const eventData = subscriber.mock.calls[0][0] + + expect(eventData.data).toMatchObject({ + entity_id: "test-metadata@medusa-commerce.com", + actor_type: "user", + token: expect.any(String), + metadata, + }) + + eventBus.unsubscribe(AuthWorkflowEvents.PASSWORD_RESET, subscriber) + }) }) it("should refresh the token successfully", async () => { diff --git a/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts b/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts index fffd2e6c54..d0c2aad405 100644 --- a/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts +++ b/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts @@ -1,3 +1,4 @@ +import type { ProjectConfigOptions } from "@medusajs/framework/types" import { AuthWorkflowEvents, generateJwtToken, @@ -9,7 +10,6 @@ import { WorkflowResponse, } from "@medusajs/framework/workflows-sdk" import { emitEventStep, useRemoteQueryStep } from "../../common" -import type { ProjectConfigOptions } from "@medusajs/framework/types" /** * This workflow generates a reset password token for a user. It's used by the @@ -48,6 +48,7 @@ export const generateResetPasswordTokenWorkflow = createWorkflow( provider: string secret: ProjectConfigOptions["http"]["jwtSecret"] jwtOptions?: ProjectConfigOptions["http"]["jwtOptions"] + metadata?: Record }) => { const providerIdentities = useRemoteQueryStep({ entry_point: "provider_identity", @@ -95,6 +96,7 @@ export const generateResetPasswordTokenWorkflow = createWorkflow( entity_id: input.entityId, actor_type: input.actorType, token, + metadata: input.metadata ?? {}, }, }) diff --git a/packages/core/utils/src/core-flows/events.ts b/packages/core/utils/src/core-flows/events.ts index 37d9e591cd..a4d416ef37 100644 --- a/packages/core/utils/src/core-flows/events.ts +++ b/packages/core/utils/src/core-flows/events.ts @@ -51,7 +51,7 @@ export const CartWorkflowEvents = { /** * Emitted when the customer in the cart is transferred. - * + * * @since 2.8.0 * * @eventPayload @@ -365,6 +365,7 @@ export const AuthWorkflowEvents = { * entity_id, // The identifier of the user or customer. For example, an email address. * actor_type, // The type of actor. For example, "customer", "user", or custom. * token, // The generated token. + * metadata, // Optional custom metadata passed from the request. * } * ``` */ @@ -937,7 +938,7 @@ export const PaymentEvents = { export const TranslationWorkflowEvents = { /** * Emitted when translations are created. - * + * * @since 2.12.3 * @featureFlag translation * @eventPayload @@ -974,4 +975,4 @@ export const TranslationWorkflowEvents = { * ``` */ DELETED: "translation.deleted", -} \ No newline at end of file +} diff --git a/packages/medusa/src/api/auth/[actor_type]/[auth_provider]/reset-password/route.ts b/packages/medusa/src/api/auth/[actor_type]/[auth_provider]/reset-password/route.ts index a59eb05d86..8d86418600 100644 --- a/packages/medusa/src/api/auth/[actor_type]/[auth_provider]/reset-password/route.ts +++ b/packages/medusa/src/api/auth/[actor_type]/[auth_provider]/reset-password/route.ts @@ -11,7 +11,7 @@ export const POST = async ( res: MedusaResponse ) => { const { auth_provider, actor_type } = req.params - const { identifier } = req.validatedBody + const { identifier, metadata } = req.validatedBody const { http } = req.scope.resolve( ContainerRegistrationKeys.CONFIG_MODULE @@ -24,6 +24,7 @@ export const POST = async ( provider: auth_provider, secret: http.jwtSecret!, jwtOptions: http.jwtOptions, + metadata, }, throwOnError: false, // we don't want to throw on error to avoid leaking information about non-existing identities }) diff --git a/packages/medusa/src/api/auth/validators.ts b/packages/medusa/src/api/auth/validators.ts index bd4a124e2a..c506cf061e 100644 --- a/packages/medusa/src/api/auth/validators.ts +++ b/packages/medusa/src/api/auth/validators.ts @@ -2,5 +2,6 @@ import { z } from "zod" export const ResetPasswordRequest = z.object({ identifier: z.string(), + metadata: z.record(z.unknown()).optional().default({}), }) export type ResetPasswordRequestType = z.infer