FeathersJS JWT Authentication Hooks: Setup, Usage, and Common Pitfalls
Learn how to configure FeathersJS to verify JWTs on every request, attach the decoded user, and avoid typical mistakes. The guide includes a full code example, a verification checklist, and production‑ready tips.
18 Jan 2026, 19:32 UTC

Why JWT Hooks Matter
FeathersJS routes incoming requests through a chain of hooks that can modify the request context or abort the operation. The @feathersjs/authentication package provides a authenticate('jwt') hook that automatically verifies a JSON Web Token (JWT) and attaches the decoded payload to context.params.user. This single hook eliminates boilerplate in every service method and guarantees that only authenticated users can perform protected actions.
Setting Up the Authentication Service
Start with a fresh Feathers app or an existing one. Install the core authentication packages:
npm install @feathersjs/authentication @feathersjs/authentication-local
Configure the authentication service in src/app.ts (or app.js if using CommonJS). Replace YOUR_SECRET with an environment variable to avoid hard‑coding secrets.
import { feathers } from '@feathersjs/feathers';
import authentication from '@feathersjs/authentication';
import authenticationLocal from '@feathersjs/authentication-local';
app.configure(authentication({
jwt: {
secret: process.env.JWT_SECRET || 'YOUR_SECRET',
expiresIn: '1h'
},
entity: 'user', // the name of the user entity
service: 'users', // the service that holds user data
local: {
usernameField: 'email',
passwordField: 'password'
}
}));
app.configure(authenticationLocal());
Key points:
- entity must match the name used by Feathers to store the authenticated user. If you use
usersas the service name, keepentity: 'user'(singular). - The
secretmust be identical to the one used when signing tokens on the client side. - Short
expiresInvalues reduce the window for replay attacks.
Attaching the Hook to Services
Decide which services require authentication. For a typical chat app, both messages and groups might be protected. Add the hook in the service file or in src/services/index.ts:
app.service('messages').hooks({
before: {
find: [authenticate('jwt')],
get: [authenticate('jwt')],
create: [authenticate('jwt')],
update: [authenticate('jwt')],
patch: [authenticate('jwt')],
remove: [authenticate('jwt')]
}
});
When a request reaches the messages service, the authenticate('jwt') hook runs before the service method. If the token is missing or invalid, the hook throws a 401 Unauthorized error; otherwise it populates context.params.user for downstream hooks or the service method itself.
Working with REST and Socket.io
Feathers supports both HTTP and WebSocket transports. The JWT hook behaves the same in both contexts, but the token must be supplied differently:
- REST: Include the token in the
Authorizationheader:Authorization: Bearer <token>. - Socket.io: Send the token during the handshake via the
authpayload:socket.emit('authenticate', { token: <token> });. After authentication, subsequent events will automatically carry the token.
Example client code (REST):
feathersClient.service('messages').find({
query: { $limit: 20 },
headers: { Authorization: 'Bearer ' + token }
});
Example client code (Socket.io):
const socket = feathersClient.configure(feathersClient.socketio('http://localhost:3030'));
const client = feathersClient.client;
client.authenticate({
strategy: 'jwt',
accessToken: token
});
Common Pitfalls & How to Avoid Them
- Wrong entity name: If
entityis set to'users'instead of'user',context.params.userwill beundefined. Verify by loggingcontext.params.userinside a downstream hook. - Secret mismatch: A typo in the secret used by the client will cause all requests to be rejected. Keep the secret in an environment file (.env) and load it with
dotenvor a secrets manager. - Missing token in Socket.io handshake: Without the
authpayload, the server will treat all events as unauthenticated. Ensure the client sends{ token: <jwt> }duringsocket.io.connect. - Unintended pagination bugs: The JWT hook does not touch query parameters. A malformed query such as
?$limit(missing a leading?) may bypass the hook if the route is mis‑configured. Validate query parameters in a separate hook if needed. - Token revocation: The hook does not automatically invalidate tokens after a password change or logout. Implement a blacklist or use short expiration times.
Practical Verification Checklist
- Generate a token: Use
feathers authentication local:loginor a custom route to sign a JWT with the same secret. - Test REST: Send a
GET /messagesrequest with theAuthorizationheader. Verify the response status is200and the request body containscontext.params.user(you can expose it in a custom hook for debugging). - Test invalid token: Replace the token with an expired or tampered string. The server should return
401 Unauthorized. - Test Socket.io: Connect with the
authpayload, then emit amessageevent. The server should process it only if the token is valid. - Check logs: Enable Feathers debug logging (
DEBUG=feathers:* npm start) to see the hook’s internal verification steps.
Limitations & Production Considerations
- HTTPS Required: Transmitting JWTs over plain HTTP exposes them to interception. Use TLS in all environments.
- Secret Rotation: Store secrets in a vault or environment manager and rotate them regularly. A rotating secret requires re‑issuing tokens to all users.
- Token Revocation: For scenarios like password changes, maintain a token blacklist or use a
tokenVersionfield in the user model that the hook checks against. - Short Expiry vs. Refresh Tokens: If you need long‑lived sessions, combine short JWTs with a refresh token flow. The hook only verifies the short token.
- Performance: The JWT hook is lightweight, but decoding large payloads or using a custom verifier can add overhead. Benchmark if you expect high request rates.
Conclusion
FeathersJS’s JWT hook streamlines authentication by centralizing token verification and user attachment. By configuring the authentication service correctly, attaching the hook to protected services, and handling transport‑specific token delivery, you can quickly secure REST and Socket.io endpoints. Remember to guard against common mistakes, enforce HTTPS, and plan for token revocation to maintain a robust security posture.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.