Use email for reset password and reset password token
This commit is contained in:
Oliver Windall Juhl
2020-05-11 13:55:44 +02:00
committed by GitHub
parent 8255a839f6
commit 809133fa2a
7 changed files with 100 additions and 55 deletions
@@ -2,26 +2,28 @@ import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request" import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user" import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id/reset-password-token", () => { describe("POST /admin/users/password-token", () => {
describe("successfully generates reset password token", () => { describe("successfully generates reset password token", () => {
let subject let subject
beforeAll(async () => { beforeAll(async () => {
subject = await request( subject = await request("POST", `/admin/users/password-token`, {
"POST", payload: {
`/admin/users/${IdMap.getId("test-user")}/reset-password-token`, email: "vandijk@test.dk",
{ },
adminSession: { adminSession: {
jwt: { jwt: {
userId: IdMap.getId("admin_user"), userId: IdMap.getId("admin_user"),
}, },
}, },
} })
)
}) })
afterAll(() => { it("calls UserService retrieve", () => {
jest.clearAllMocks() expect(UserServiceMock.retrieveByEmail).toHaveBeenCalledTimes(1)
expect(UserServiceMock.retrieveByEmail).toHaveBeenCalledWith(
"vandijk@test.dk"
)
}) })
it("calls UserService generateResetPasswordToken", () => { it("calls UserService generateResetPasswordToken", () => {
@@ -29,12 +31,12 @@ describe("POST /admin/users/:id/reset-password-token", () => {
1 1
) )
expect(UserServiceMock.generateResetPasswordToken).toHaveBeenCalledWith( expect(UserServiceMock.generateResetPasswordToken).toHaveBeenCalledWith(
IdMap.getId("test-user") IdMap.getId("vandijk")
) )
}) })
it("returns 200", () => { it("returns 204", () => {
expect(subject.status).toEqual(200) expect(subject.status).toEqual(204)
}) })
}) })
}) })
@@ -3,7 +3,7 @@ import jwt from "jsonwebtoken"
import { request } from "../../../../../helpers/test-request" import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user" import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id/reset-password", () => { describe("POST /admin/users/reset-password", () => {
describe("successfully resets password", () => { describe("successfully resets password", () => {
let subject let subject
@@ -13,22 +13,19 @@ describe("POST /admin/users/:id/reset-password", () => {
it("calls UserService setPassword", async () => { it("calls UserService setPassword", async () => {
const exp = Math.floor(Date.now() / 1000) + 60 * 15 const exp = Math.floor(Date.now() / 1000) + 60 * 15
const token = jwt.sign(
subject = await request("POST", `/admin/users/reset-password`, {
payload: {
email: "vandijk@test.dk",
token: jwt.sign(
{ {
userId: "test-user-id", user_id: IdMap.getId("vandijk"),
name: "Oliver Juhl", name: "Virgil Van Dijk",
email: "oliver@test.dk", email: "vandijk@test.dk",
exp, exp,
}, },
"123456789hash" "1234"
) ),
subject = await request(
"POST",
`/admin/users/test-user-id/reset-password`,
{
payload: {
token,
password: "new-password", password: "new-password",
}, },
adminSession: { adminSession: {
@@ -36,19 +33,17 @@ describe("POST /admin/users/:id/reset-password", () => {
userId: IdMap.getId("admin_user"), userId: IdMap.getId("admin_user"),
}, },
}, },
} })
)
expect(UserServiceMock.setPassword).toHaveBeenCalledTimes(1) expect(UserServiceMock.setPassword).toHaveBeenCalledTimes(1)
expect(UserServiceMock.setPassword).toHaveBeenCalledWith( expect(UserServiceMock.setPassword).toHaveBeenCalledWith(
"test-user-id", IdMap.getId("vandijk"),
"new-password" "new-password"
) )
}) })
it("returns updated user", () => { it("returns updated user", () => {
expect(subject.body._id).toEqual("test-user-id") expect(subject.body._id).toEqual(IdMap.getId("vandijk"))
}) })
it("returns 200", () => { it("returns 200", () => {
expect(subject.status).toEqual(200) expect(subject.status).toEqual(200)
}) })
@@ -10,20 +10,20 @@ export default app => {
route.get("/:user_id", middlewares.wrap(require("./get-user").default)) route.get("/:user_id", middlewares.wrap(require("./get-user").default))
route.post("/", middlewares.wrap(require("./create-user").default)) route.post("/", middlewares.wrap(require("./create-user").default))
route.post("/:user_id", middlewares.wrap(require("./update-user").default))
route.post( route.post(
"/:user_id/set-password", "/password-token",
middlewares.wrap(require("./set-password").default)
)
route.post(
"/:user_id/reset-password-token",
middlewares.wrap(require("./reset-password-token").default) middlewares.wrap(require("./reset-password-token").default)
) )
route.post( route.post(
"/:user_id/reset-password", "/reset-password",
middlewares.wrap(require("./reset-password").default) middlewares.wrap(require("./reset-password").default)
) )
route.post("/:user_id", middlewares.wrap(require("./update-user").default))
route.post(
"/:user_id/set-password",
middlewares.wrap(require("./set-password").default)
)
route.delete("/:user_id", middlewares.wrap(require("./delete-user").default)) route.delete("/:user_id", middlewares.wrap(require("./delete-user").default))
@@ -1,9 +1,24 @@
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => { export default async (req, res) => {
const { user_id } = req.params const schema = Validator.object().keys({
email: Validator.string()
.email()
.required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try { try {
const userService = req.scope.resolve("userService") const userService = req.scope.resolve("userService")
const token = await userService.generateResetPasswordToken(user_id) const user = await userService.retrieveByEmail(value.email)
res.json(token)
await userService.generateResetPasswordToken(user._id)
res.sendStatus(204)
} catch (error) { } catch (error) {
throw error throw error
} }
@@ -2,8 +2,10 @@ import { MedusaError, Validator } from "medusa-core-utils"
import jwt from "jsonwebtoken" import jwt from "jsonwebtoken"
export default async (req, res) => { export default async (req, res) => {
const { user_id } = req.params
const schema = Validator.object().keys({ const schema = Validator.object().keys({
email: Validator.string()
.email()
.required(),
token: Validator.string().required(), token: Validator.string().required(),
password: Validator.string().required(), password: Validator.string().required(),
}) })
@@ -15,11 +17,12 @@ export default async (req, res) => {
try { try {
const userService = req.scope.resolve("userService") const userService = req.scope.resolve("userService")
let user = await userService.retrieve(user_id) let user = await userService.retrieveByEmail(value.email)
const decodedToken = await jwt.verify(value.token, user.password_hash) const decodedToken = await jwt.verify(value.token, user.password_hash)
if (!decodedToken) { if (!decodedToken || decodedToken.user_id !== user._id) {
res.status(401).send("Invalid or expired password reset token") res.status(401).send("Invalid or expired password reset token")
return
} }
await userService.setPassword(user._id, value.password) await userService.setPassword(user._id, value.password)
@@ -8,6 +8,11 @@ export const users = {
email: "oliver@test.dk", email: "oliver@test.dk",
password_hash: "hashed123456789", password_hash: "hashed123456789",
}, },
vanDijk: {
_id: IdMap.getId("vandijk"),
email: "vandijk@test.dk",
password_hash: "hashed123456789",
},
jwtUser: { jwtUser: {
_id: "test-user-id", _id: "test-user-id",
email: "oliver@test.dk", email: "oliver@test.dk",
@@ -43,6 +48,9 @@ export const UserServiceMock = {
if (userId === IdMap.getId("test-user")) { if (userId === IdMap.getId("test-user")) {
return Promise.resolve(users.testUser) return Promise.resolve(users.testUser)
} }
if (userId === IdMap.getId("vandijk")) {
return Promise.resolve(users.vanDijk)
}
// used for jwt token tests // used for jwt token tests
if (userId === "test-user-id") { if (userId === "test-user-id") {
return Promise.resolve(users.jwtUser) return Promise.resolve(users.jwtUser)
@@ -66,6 +74,13 @@ export const UserServiceMock = {
return Promise.resolve(undefined) return Promise.resolve(undefined)
}), }),
retrieveByEmail: jest.fn().mockImplementation(email => { retrieveByEmail: jest.fn().mockImplementation(email => {
if (email === "vandijk@test.dk") {
return Promise.resolve({
_id: IdMap.getId("vandijk"),
email,
password_hash: "1234",
})
}
if (email === "oliver@test.dk") { if (email === "oliver@test.dk") {
return bcrypt return bcrypt
.hash("123456789", 10) .hash("123456789", 10)
+15
View File
@@ -4522,6 +4522,14 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
medusa-core-utils@^0.3.0:
version "0.1.39"
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df"
integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ==
dependencies:
"@hapi/joi" "^16.1.8"
joi-objectid "^3.0.1"
medusa-interfaces@^0.1.27: medusa-interfaces@^0.1.27:
version "0.1.27" version "0.1.27"
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898" resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
@@ -4529,6 +4537,13 @@ medusa-interfaces@^0.1.27:
dependencies: dependencies:
mongoose "^5.8.0" mongoose "^5.8.0"
medusa-test-utils@^0.3.0:
version "0.1.39"
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e"
integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw==
dependencies:
mongoose "^5.8.0"
memory-pager@^1.0.2: memory-pager@^1.0.2:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"