Feathers: context.params.user is missing in before hook

Created on 10 Jun 2019  路  9Comments  路  Source: feathersjs/feathers

Steps to reproduce

  1. Create FeathersJs project with PostgreSQL database
  2. Create db migrations for users table
  3. Run migrations using sequalize.js (db:migrate)
  4. Setup featherjs config
    { "host": "localhost", "port": 3030, "public": "../public/", "paginate": { "default": 10, "max": 50 }, "authentication": { "secret": "SOME-KEY", "strategies": [ "local", "jwt" ], "path": "/authentication", "service": "users", "jwt": { "header": { "typ": "access" }, "audience": "https://yourdomain.com", "subject": "anonymous", "issuer": "feathers", "algorithm": "HS256", "expiresIn": "1d" }, "local": { "entity": "user", "usernameField": "email", "passwordField": "password" } }, "postgres": "postgresql://dbadmin:mydb@localhost:5432/mydb_table" }
  5. Get valid access token from /authentication (POST)
    Example response:
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJ1c2VySWQiOiIzMzAyM2I3ZS1iOTZhLTRhODQtYjJjOS01ZTc4OWQxMmFhMzUiLCJpYXQiOjE1NjAxNjMyOTQsImV4cCI6MTU2MDI0OTY5NCwiYXVkIjoiaHR0cHM6Ly95b3VyZG9tYWluLmNvbSIsImlzcyI6ImZlYXRoZXJzIiwic3ViIjoiYW5vbnltb3VzIiwianRpIjoiNmRkYzc2N2MtODFkMC00OTYzLTk3MDItNjY3Nzc3YTVkNDVlIn0.DecMVpm0mYeYa8ckco9fHg1rLieCzB3CBTS8XMKYUJk"
}
  1. In users hooks:
    6.1 In hook before.all add authenticate('jwt')
    6.2 In before.get add filterUser() hook
  2. filterUser hook:
    // eslint-disable-next-line no-unused-vars
    const _ = require('lodash');
    module.exports = function (options = {}) {
    return async context => {
    const user = _.get(context.params, 'user', {});
    console.log('context', context.params);
    context.result = user;
    return context;
    };
    };
    `
  3. Validate that Postgre database users table is not empty and has users.
  4. Run a user get request eg. http://localhost:3030/users/33023b7e-b96a-4a84-b2c9-5e789d12aa35
  5. See that context.params.user is empty, even with before.all hook being, all: [authenticate('jwt')], or even if authenticate('jwt') is added on the same line as userFilter hook.

Expected behavior

Should return a user object, eg.
{ "id": "33023b7e-b96a-4a84-b2c9-5e789d12aa35", "first_name": "Alex", "last_name": "D", "email": "[email protected]", "is_verified": false }

Actual behavior

Returns an empty object {}

System configuration

"@feathersjs/authentication": "2.1.16",
"@feathersjs/authentication-jwt": "^2.0.7",
"@feathersjs/authentication-local": "^1.2.7",
"@feathersjs/configuration": "^2.0.4",
"@feathersjs/errors": "^3.3.4",
"@feathersjs/express": "^1.2.7",
"@feathersjs/feathers": "^3.2.3",

"engines": {
"node": "^12.0.0",
"yarn": ">= 0.18.0"
},

Tell us about the applicable parts of your setup.

NodeJS version:
12.0.0
Operating System:
MacOS 10.13.6

Most helpful comment

The authenticate('jwt') hook needs to do a second get request on the users service without any parameters in order to retrieve the actual object to set as params.user. So you will see two, one without first (it is an internal call because params.provider will be undefined) and then a second get with params.user set.

All 9 comments

Decoded token:
{
"userId": "33023b7e-b96a-4a84-b2c9-5e789d12aa35",
"iat": 1560163294,
"exp": 1560249694,
"aud": "https://yourdomain.com",
"iss": "feathers",
"sub": "anonymous",
"jti": "6ddc767c-81d0-4963-9702-667777a5d45e"
}

The authenticate('jwt') hook needs to do a second get request on the users service without any parameters in order to retrieve the actual object to set as params.user. So you will see two, one without first (it is an internal call because params.provider will be undefined) and then a second get with params.user set.

The authenticate('jwt') hook needs to do a second get request on the users service without any parameters in order to retrieve the actual object to set as params.user. So you will see two, one without first (it is an internal call because params.provider will be undefined) and then a second get with params.user set.

Solved it by changing userFillter hook to
module.exports = function (options = {}) {
return async context => {
context.result = context.params.user;
return context;
};
};

Any idea why it does NOT work if
context.result = context.params.user || {};
OR
context.result = _.get(context.params, 'user', {});

very strange

Your example token does not include the userId as the payload (check at jwt.io that it does). This is usually the case when the authentication module is not configured properly.

@daffl @fireflypie I'm experiencing the same problem with postgres. I've read over several old issues and have had no success with this. Could you elaborate on properly configuring the authentication module? What was the solution?

my decoded jwt:
{ "iat": 1588539863, "exp": 1588626263, "aud": "https://yourdomain.com", "iss": "feathers", "sub": "26", "jti": "ecfd0e55-2b27-4834-97fd-cff19ea9a524" }

sub is the id of the user I'm logged in as.

The hook
module.exports = (options = {}) => { return async context => { context.result = context.params.user; return context; }; };

there is never a context.params.user object and result is always undefined

Please create a new issue ideally with a link to a complete minimal repository to reproduce the problem.

@daffl @fireflypie I'm experiencing the same problem with postgres. I've read over several old issues and have had no success with this. Could you elaborate on properly configuring the authentication module? What was the solution?

my decoded jwt:
{ "iat": 1588539863, "exp": 1588626263, "aud": "https://yourdomain.com", "iss": "feathers", "sub": "26", "jti": "ecfd0e55-2b27-4834-97fd-cff19ea9a524" }

sub is the id of the user I'm logged in as.

The hook
module.exports = (options = {}) => { return async context => { context.result = context.params.user; return context; }; };

there is never a context.params.user object and result is always undefined

I had a similar issue and I was able to fix it by bringing the authenticate hook before my processProperty hook as shown below.
I think this is because each hook in the array is processed from left to right, so in this case, the processProperty hook needs access to the authenticated user, so it needs to come after the authenticate hook has been processed.
module.exports = { before: { ... create: [authenticate('jwt'), processProperty()], ... } }

The authenticate('jwt') hook needs to do a second get request on the users service without any parameters in order to retrieve the actual object to set as params.user. So you will see two, one without first (it is an internal call because params.provider will be undefined) and then a second get with params.user set.

I've spent like 4 hours or more until I've read this explanation and now I understand why I've always was getting errors when trying to access any property from the context.params.user object (like theid , for instance) from my own custom "before get hook". Since the hook gets fired two times, the first time with authenticate ('jwt') as you pointed the user object is not still there, the second and other ones the context.params.user object is there.

So, I've solved the problem checking for the user property from thecontext.params object before trying to use it.

Thanks for your explanation!

If you are using middleware to call a service, this could be the problem

My problem was
service.find({query,params:req.feathers}).then(...).
in this case context.params.user is undefined

But what worked for me is
service.find({query,...req.feathers}).then(...)

Was this page helpful?
0 / 5 - 0 ratings