A hook that helps limiting and associating user requests
$ npm install feathers-authentication-hooks --save
feathers-authentication-hooks
contains a Feathers hook that allows to set query or data properties based on other existing properties (like an authenticated user or organization id).
Note:
feathers-authentication-hooks
v1.0.0 and later requires Feathers 4 or later.
The setField
hook allows to set a field on the hook context based on the value of another field on the hook context.
from
required - The property on the hook context to use. Can be an array (e.g.[ 'params', 'user', 'id' ]
) or a dot separated string (e.g.'params.user.id'
).as
required - The property on the hook context to set. Can be an array (e.g.[ 'params', 'query', 'userId' ]
) or a dot separated string (e.g.'params.query.userId'
).allowUndefined
(default:false
) - If set tofalse
, an error will be thrown if the value offrom
isundefined
in an external request (params.provider
is set). On internal calls (or if set to truetrue
for external calls) the hook will do nothing.
Important: This hook should be used after the authenticate hook when accessing user fields (from
params.user
).
Limit all external access of the users
service to the authenticated user:
Note: For MongoDB, Mongoose and NeDB
params.user.id
needs to be changed toparams.user._id
. For any other custom id accordingly.
const { authenticate } = require('@feathersjs/authentication');
const { setField } = require('feathers-authentication-hooks');
app.service('users').hooks({
before: {
all: [
authenticate('jwt'),
setField({
from: 'params.user.id',
as: 'params.query.id'
})
]
}
})
Only allow access to invoices for the users organization:
const { authenticate } = require('@feathersjs/authentication');
const { setField } = require('feathers-authentication-hooks');
app.service('invoices').hooks({
before: {
all: [
authenticate('jwt'),
setField({
from: 'params.user.organizationId',
as: 'params.query.organizationId'
})
]
}
})
Set the current user id as userId
when creating a message and only allow users to edit and remove their own messages:
const { authenticate } = require('@feathersjs/authentication');
const { setField } = require('feathers-authentication-hooks');
const setUserId = setField({
from: 'params.user.id',
as: 'data.userId'
});
const limitToUser = setField({
from: 'params.user.id',
as: 'params.query.userId'
});
app.service('messages').hooks({
before: {
all: [
authenticate('jwt')
],
create: [
setUserId
],
patch: [
limitToUser
],
update: [
limitToUser
]
remove: [
limitToUser
]
}
})
The previous versions of feathers-authentication-hooks
contained several hooks that required more detailed configuration and knowledge about the application and authentication. Due to improvements in the database adapters in Feathers 4 those hooks can now all be replaced with the setField
hook and a more explicit configuration.
Before:
const hooks = require('feathers-authentication-hooks');
app.service('messages').before({
find: [
hooks.queryWithCurrentUser({ idField: 'id', as: 'sentBy' })
]
});
Now:
const { setField } = require('feathers-authentication-hooks');
app.service('messages').before({
find: [
setField({
from: 'params.user.id',
as: 'params.query.sentBy'
})
]
});
Dot separated paths in queries (previously with the expandPaths
option) are possible by passing an array as the field name (using Lodash _.set internally):
const { setField } = require('feathers-authentication-hooks');
app.service('messages').before({
find: [
setField({
from: 'params.user.id',
as: [ 'params', 'query', 'nested.document.sentBy' ]
})
]
});
Due to improvements in the Feathers 4 database adapters restricting to an owner works the exact same as a queryWithCurrentUser
. It will now throw a NotFound
instead of a Forbidden
error which is also more secure since an unauthorized user does not get the information if the record exists and no longer make an additional request.
Before:
const hooks = require('feathers-authentication-hooks');
app.service('messages').before({
remove: [
hooks.restrictToOwner({ idField: 'id', ownerField: 'sentBy' })
]
});
Now:
const { setField } = require('feathers-authentication-hooks');
app.service('messages').before({
remove: [
setField({
from: 'params.user.id',
as: 'params.query.userId'
})
]
});
The associateCurrentUser
can also be replaced by setField
by setting data
instead of params.query.*
Before:
const hooks = require('feathers-authentication-hooks');
app.service('messages').before({
create: [
hooks.associateCurrentUser({ idField: 'id', as: 'sentBy' })
]
});
Now:
const { setField } = require('feathers-authentication-hooks');
app.service('messages').before({
create: [
setField({
from: 'params.user.id',
as: 'data.sentBy'
})
]
});
Copyright (c) 2019
Licensed under the MIT license.