fix(medusa): Error messages for reset tokens (#3514)
* initial * reset password token handling * Create .changeset/old-planes-cross.md --------- Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
5
.changeset/old-planes-cross.md
Normal file
5
.changeset/old-planes-cross.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): Error messages for reset tokens
|
||||
@@ -171,6 +171,16 @@ describe("/admin/users", () => {
|
||||
})
|
||||
|
||||
describe("Password reset", () => {
|
||||
it("Doesn't fail to fetch user when resetting password for an unknown email (unauthorized endpoint)", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const resp = await api.post("/admin/users/password-token", {
|
||||
email: "test-doesnt-exist@test.com",
|
||||
})
|
||||
|
||||
expect(resp.status).toEqual(204)
|
||||
})
|
||||
|
||||
it("Doesn't fail when generating password reset token (unauthorized endpoint)", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
@@ -521,5 +521,40 @@ describe("/store/customers", () => {
|
||||
|
||||
expect(response.status).toEqual(204)
|
||||
})
|
||||
|
||||
it("Returns 204 for non-existent customer", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.post(`/store/customers/password-token`, {
|
||||
email: "non-existent@test.com",
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(204)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /store/customers/password-reset", () => {
|
||||
afterEach(async () => {
|
||||
await doAfterEach()
|
||||
})
|
||||
|
||||
it("Returns 204 for non-existent customer", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(`/store/customers/password-reset`, {
|
||||
email: "non-existent@test.com",
|
||||
token: "token",
|
||||
password: "password",
|
||||
})
|
||||
.catch((error) => {
|
||||
return error
|
||||
})
|
||||
expect(response.response.status).toEqual(401)
|
||||
expect(response.response.data).toEqual({
|
||||
type: "unauthorized",
|
||||
message: "Invalid or expired password reset token",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -66,15 +66,19 @@ export default async (req, res) => {
|
||||
const validated = await validator(AdminResetPasswordTokenRequest, req.body)
|
||||
|
||||
const userService: UserService = req.scope.resolve("userService")
|
||||
const user = await userService.retrieveByEmail(validated.email)
|
||||
const user = await userService
|
||||
.retrieveByEmail(validated.email)
|
||||
.catch(() => undefined)
|
||||
|
||||
// Should call a email service provider that sends the token to the user
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await userService
|
||||
.withTransaction(transactionManager)
|
||||
.generateResetPasswordToken(user.id)
|
||||
})
|
||||
if (user) {
|
||||
// Should call a email service provider that sends the token to the user
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await userService
|
||||
.withTransaction(transactionManager)
|
||||
.generateResetPasswordToken(user.id)
|
||||
})
|
||||
}
|
||||
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
||||
@@ -66,17 +66,19 @@ export default async (req, res) => {
|
||||
"customerService"
|
||||
) as CustomerService
|
||||
|
||||
const customer = await customerService.retrieveRegisteredByEmail(
|
||||
validated.email
|
||||
)
|
||||
const customer = await customerService
|
||||
.retrieveRegisteredByEmail(validated.email)
|
||||
.catch(() => undefined)
|
||||
|
||||
// Will generate a token and send it to the customer via an email provider
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await customerService
|
||||
.withTransaction(transactionManager)
|
||||
.generateResetPasswordToken(customer.id)
|
||||
})
|
||||
if (customer) {
|
||||
// Will generate a token and send it to the customer via an email provider
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await customerService
|
||||
.withTransaction(transactionManager)
|
||||
.generateResetPasswordToken(customer.id)
|
||||
})
|
||||
}
|
||||
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import jwt, { JwtPayload } from "jsonwebtoken"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* @oas [post] /store/customers/password-reset
|
||||
@@ -70,20 +71,25 @@ export default async (req, res) => {
|
||||
)) as StorePostCustomersResetPasswordReq
|
||||
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
let customer = await customerService.retrieveRegisteredByEmail(
|
||||
validated.email,
|
||||
{
|
||||
select: ["id", "password_hash"],
|
||||
}
|
||||
)
|
||||
let customer
|
||||
|
||||
const decodedToken = jwt.verify(
|
||||
validated.token,
|
||||
customer.password_hash
|
||||
) as JwtPayload
|
||||
if (!decodedToken || customer.id !== decodedToken.customer_id) {
|
||||
res.status(401).send("Invalid or expired password reset token")
|
||||
return
|
||||
customer = await customerService
|
||||
.retrieveRegisteredByEmail(validated.email, {
|
||||
select: ["id", "password_hash"],
|
||||
})
|
||||
.catch(() => undefined)
|
||||
|
||||
const decodedToken = customer
|
||||
? jwt.verify(validated.token, customer.password_hash)
|
||||
: undefined
|
||||
if (
|
||||
!decodedToken ||
|
||||
customer.id !== (decodedToken as JwtPayload)?.customer_id
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.UNAUTHORIZED,
|
||||
"Invalid or expired password reset token"
|
||||
)
|
||||
}
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
Reference in New Issue
Block a user