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
>
> <sup>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).</sup>
This commit is contained in:
1 parent
d06729d0d1
commit
43305a562c
6 files changed
+58
-5
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,utils,core-flows): add reset password metdata
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { generateResetPasswordTokenWorkflow } from "@medusajs/core-flows"
|
import { generateResetPasswordTokenWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { AuthWorkflowEvents, Modules } from "@medusajs/framework/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
import {
|
import {
|
||||||
@@ -431,6 +432,46 @@ medusaIntegrationTestRunner({
|
|||||||
expect(response.response.status).toEqual(401)
|
expect(response.response.status).toEqual(401)
|
||||||
expect(response.response.data.message).toEqual("Invalid token")
|
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 () => {
|
it("should refresh the token successfully", async () => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { ProjectConfigOptions } from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
AuthWorkflowEvents,
|
AuthWorkflowEvents,
|
||||||
generateJwtToken,
|
generateJwtToken,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
WorkflowResponse,
|
WorkflowResponse,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { emitEventStep, useRemoteQueryStep } from "../../common"
|
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
|
* 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
|
provider: string
|
||||||
secret: ProjectConfigOptions["http"]["jwtSecret"]
|
secret: ProjectConfigOptions["http"]["jwtSecret"]
|
||||||
jwtOptions?: ProjectConfigOptions["http"]["jwtOptions"]
|
jwtOptions?: ProjectConfigOptions["http"]["jwtOptions"]
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
}) => {
|
}) => {
|
||||||
const providerIdentities = useRemoteQueryStep({
|
const providerIdentities = useRemoteQueryStep({
|
||||||
entry_point: "provider_identity",
|
entry_point: "provider_identity",
|
||||||
@@ -95,6 +96,7 @@ export const generateResetPasswordTokenWorkflow = createWorkflow(
|
|||||||
entity_id: input.entityId,
|
entity_id: input.entityId,
|
||||||
actor_type: input.actorType,
|
actor_type: input.actorType,
|
||||||
token,
|
token,
|
||||||
|
metadata: input.metadata ?? {},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const CartWorkflowEvents = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the customer in the cart is transferred.
|
* Emitted when the customer in the cart is transferred.
|
||||||
*
|
*
|
||||||
* @since 2.8.0
|
* @since 2.8.0
|
||||||
*
|
*
|
||||||
* @eventPayload
|
* @eventPayload
|
||||||
@@ -365,6 +365,7 @@ export const AuthWorkflowEvents = {
|
|||||||
* entity_id, // The identifier of the user or customer. For example, an email address.
|
* 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.
|
* actor_type, // The type of actor. For example, "customer", "user", or custom.
|
||||||
* token, // The generated token.
|
* token, // The generated token.
|
||||||
|
* metadata, // Optional custom metadata passed from the request.
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
@@ -937,7 +938,7 @@ export const PaymentEvents = {
|
|||||||
export const TranslationWorkflowEvents = {
|
export const TranslationWorkflowEvents = {
|
||||||
/**
|
/**
|
||||||
* Emitted when translations are created.
|
* Emitted when translations are created.
|
||||||
*
|
*
|
||||||
* @since 2.12.3
|
* @since 2.12.3
|
||||||
* @featureFlag translation
|
* @featureFlag translation
|
||||||
* @eventPayload
|
* @eventPayload
|
||||||
@@ -974,4 +975,4 @@ export const TranslationWorkflowEvents = {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
DELETED: "translation.deleted",
|
DELETED: "translation.deleted",
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ export const POST = async (
|
|||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const { auth_provider, actor_type } = req.params
|
const { auth_provider, actor_type } = req.params
|
||||||
const { identifier } = req.validatedBody
|
const { identifier, metadata } = req.validatedBody
|
||||||
|
|
||||||
const { http } = req.scope.resolve(
|
const { http } = req.scope.resolve(
|
||||||
ContainerRegistrationKeys.CONFIG_MODULE
|
ContainerRegistrationKeys.CONFIG_MODULE
|
||||||
@@ -24,6 +24,7 @@ export const POST = async (
|
|||||||
provider: auth_provider,
|
provider: auth_provider,
|
||||||
secret: http.jwtSecret!,
|
secret: http.jwtSecret!,
|
||||||
jwtOptions: http.jwtOptions,
|
jwtOptions: http.jwtOptions,
|
||||||
|
metadata,
|
||||||
},
|
},
|
||||||
throwOnError: false, // we don't want to throw on error to avoid leaking information about non-existing identities
|
throwOnError: false, // we don't want to throw on error to avoid leaking information about non-existing identities
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ import { z } from "zod"
|
|||||||
|
|
||||||
export const ResetPasswordRequest = z.object({
|
export const ResetPasswordRequest = z.object({
|
||||||
identifier: z.string(),
|
identifier: z.string(),
|
||||||
|
metadata: z.record(z.unknown()).optional().default({}),
|
||||||
})
|
})
|
||||||
export type ResetPasswordRequestType = z.infer<typeof ResetPasswordRequest>
|
export type ResetPasswordRequestType = z.infer<typeof ResetPasswordRequest>
|
||||||
Reference in new issue
Block a user