feathersjs-ecosystem / feathers-authentication-management

Adds sign up verification, forgotten password reset, and other capabilities to local feathers-authentication
https://feathers-a-m.netlify.app/
MIT License
247 stars 98 forks source link

PW Reset goews awry - doesnt change the pw to the supposed value but anything else.. #149

Closed andreas-it-dev closed 4 years ago

andreas-it-dev commented 4 years ago

Steps to reproduce

Hi,

idk if it is me or the auth management, but when i reset the password, it wont change it to the desired value but anthing else, ending in a situation where i still cant sign in. here is a walk through made with feathers and postman:

i got the reset token and try to reset the pw:

{
    "action": "resetPwdLong",
    "value": {
        "token": "5f11af9556667018746dd417___c20a6535d896d19036994b46c6df30",
        "password": "supersecretpassword"
    }
}

i get the all clear message:

{
    "_id": "5f11af9556667018746dd417",
    "username": "username",
    "name": "name",
    "email": "email@example.com",
    "theme": "dark",
    "isVerified": true,
    "createdAt": "2020-07-17T14:03:01.849Z",
    "updatedAt": "2020-07-19T07:34:51.180Z",
    "__v": 0
}

now, when trying to sign-in:

{
  "strategy": "local",
  "username": "username",
  "password": "supersecretpassword"
}

he wont let me in (not even with the old pasword)

{
    "name": "NotAuthenticated",
    "message": "Invalid login",
    "code": 401,
    "className": "not-authenticated",
    "errors": {}
}

i know the correct password is sent to the backend:

express:router <anonymous>  : /authmanagement +0ms
  @feathersjs/authentication/base Strategies parsing HTTP header for authentication information [ 'jwt', 'local' ] +6m
  @feathersjs/express/rest REST handler calling `create` from `/authmanagement` +6m
  authLocalMgnt:service create called. action=resetPwdLong +6m
  authLocalMgnt:resetPassword resetPassword {
  resetToken: '5f11af9556667018746dd417___c20a6535d896d19036994b46c6df30'
} {
  resetToken: '5f11af9556667018746dd417___c20a6535d896d19036994b46c6df30'
} supersecretpassword +12m
  @feathersjs/authentication/hooks/authenticate Running authenticate hook on 'users' +6m
  mquery findOne users { '$and': [ { _id: 5f11af9556667018746dd417 } ] } { session: undefined, projection: {} } +6m
  @feathersjs/authentication/hooks/authenticate Running authenticate hook on 'users' +175ms
  mquery findOne users { '$and': [ { _id: 5f11af9556667018746dd417 } ] } { session: undefined, projection: {} } +175ms
  mquery findOne users {
  _id: { '$in': [ 5f11af9556667018746dd417 ] },
  '$and': [ { _id: 5f11af9556667018746dd417 } ]
} { session: undefined, projection: {} } +3ms
  @feathersjs/transport-commons/channels Publishing event patched users +6m
however, after that i can no longer sigin in, not with the old password and not with the new one..
however, what looks little weird is this:
authLocalMgnt:resetPassword resetPassword {
  resetToken: '5f11af9556667018746dd417___c20a6535d896d19036994b46c6df30'
} {
  resetToken: '5f11af9556667018746dd417___c20a6535d896d19036994b46c6df30'
} supersecretpassword

bonus question: is it supposed to have the reset token twice and only once with my new passord?

Expected behavior

it should reset the password to the desired value

Actual behavior

it changes it to some unknown value

System configuration

Module versions (especially the part that's not working): see my package.json:

{
  "name": "some name",
  "description": "",
  "version": "0.0.0",
  "homepage": "",
  "private": true,
  "main": "src",
  "keywords": [
    "feathers"
  ],
  "author": {
    "name": "",
    "email": ""
  },
  "contributors": [],
  "bugs": {},
  "directories": {
    "lib": "src",
    "test": "test/",
    "config": "config/"
  },
  "engines": {
    "node": "^12.0.0",
    "yarn": ">= 0.18.0"
  },
  "scripts": {
    "test": "yarn run lint && yarn run mocha",
    "lint": "eslint src/. test/. --config .eslintrc.json",
    "dev": "nodemon src/",
    "start": "node src/",
    "debug": "set DEBUG=* node src/",
    "mocha": "mocha test/ --recursive --exit"
  },
  "standard": {
    "env": [
      "mocha"
    ],
    "ignore": []
  },
  "dependencies": {
    "@feathersjs/authentication": "^4.5.1",
    "@feathersjs/authentication-local": "^4.5.1",
    "@feathersjs/authentication-oauth": "^4.5.1",
    "@feathersjs/configuration": "^4.5.1",
    "@feathersjs/errors": "^4.5.1",
    "@feathersjs/express": "^4.5.1",
    "@feathersjs/feathers": "^4.5.1",
    "@feathersjs/socketio": "^4.5.1",
    "@feathersjs/transport-commons": "^4.5.5",
    "compression": "^1.7.4",
    "cors": "^2.8.5",
    "feathers-authentication-management": "^3.0.0",
    "feathers-hooks-common": "^5.0.3",
    "feathers-mailer": "^3.0.1",
    "feathers-mongoose": "^8.3.0",
    "helmet": "^3.21.2",
    "mongodb-core": "^3.2.7",
    "mongoose": "^5.8.13",
    "nodemailer-smtp-transport": "^2.7.4",
    "prettier": "^2.0.5",
    "serve-favicon": "^2.5.0",
    "winston": "^3.0.0"
  },
  "devDependencies": {
    "axios": "^0.19.2",
    "eslint": "^7.4.0",
    "mocha": "^7.0.1",
    "nodemon": "^2.0.2"
  }
}

NodeJS version: 12.18.1

Operating System: Windows 10

Browser Version: Chrome 83.0.4103.116 (Official Build) (64-bit)

OnnoGabriel commented 4 years ago

Hi @awunder, have you checked the password value in the database before and after the password reset?

Could it be that the new password is not hashed at all? Do you use hashPassword('password') in the patch method of your users service hooks (e.g. in services/users/users.hook.js)?

andreas-it-dev commented 4 years ago

hey @OnnoGabriel ,

thanks for the quick response.. yes, i do use those methods in my users.hooks.js:

const { authenticate } = require("@feathersjs/authentication").hooks;
const verifyHooks = require("feathers-authentication-management").hooks;
const accountService = require("../authmanagement/notifier");

const {
  hashPassword,
  protect,
} = require("@feathersjs/authentication-local").hooks;

module.exports = {
  before: {
    all: [],
    find: [authenticate("jwt")],
    get: [authenticate("jwt")],
    create: [hashPassword("password"), verifyHooks.addVerification()],
    update: [hashPassword("password"), authenticate("jwt")],
    patch: [hashPassword("password"), authenticate("jwt")],
    remove: [authenticate("jwt")],
  },

  after: {
    all: [
      // Make sure the password field is never sent to the client
      // Always must be the last hook
      protect("password"),
    ],
    find: [],
    get: [],
    create: [
      (context) => {
        accountService(context.app).notifier(
          "resendVerifySignup",
          context.data
        );
      },
      verifyHooks.removeVerification(),
    ],
    update: [],
    patch: [],
    remove: [],
  },

  error: {
    all: [],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: [],
  },
};

and the the pw is getting hashed and also changed. this is a recent example:

before requesting a pw-reset:

image

after making the request:

image

after changing the password:

image

appreciate your help, Andreas

OnnoGabriel commented 4 years ago

Ah, ok, it seems that you have to extend your users.hooks.js. The reason is, that feather-authentication-management is already hashing the new password. Then it saves the hashed password via the patch method of your user service, where (in your case) it is hashed again. To prevent this, you can limit hashPassword('password') to external calls. Thus, the password will not be hashed if feather-authentication-management calls the user service internally. For example:

const { hashPassword, protect } = require('@feathersjs/authentication-local').hooks
const verifyHooks = require("feathers-authentication-management").hooks;
const { iff, isProvider, preventChanges } = require('feathers-hooks-common')

[...]

patch: [
  iff(
    isProvider('external'),
    preventChanges(
      true,
      'email',
      'isVerified',
      'verifyToken',
      'verifyShortToken',
      'verifyExpires',
      'verifyChanges',
      'resetToken',
      'resetShortToken',
      'resetExpires'
    ),
    authenticate('jwt'),
    hashPassword('password')
 ]

See https://hackernoon.com/setting-up-email-verification-in-feathersjs-ce764907e4f2 for more details.

andreas-it-dev commented 4 years ago

fun fact: i followed that turorial until the last step.. i wanted to make it work though, before i bring in security :D

anyway, that did the trick, thanks a lot Onno!

OnnoGabriel commented 4 years ago

I'm glad I could help, Andreas.

I remember similar problems, when I started with this package. We should probably update the docs.