feathers
feathers copied to clipboard
Authenticate another service v4
I have authentication for 'users' service and I'd like to authenticate 'systems' (which is another service I generated) with both local and jwt strategies.
how do I go about doing that?
I was able to do that with v3 by modifying default.json as I did in #1078
I am failing to do that with v4 Kindly assist!
System configuration
default.json
{
"host": "0.0.0.0",
"port": 3030,
"public": "../public/",
"paginate": {
"default": 10,
"max": 50
},
"authentication": {
"entity": "user",
"service": "users",
"secret": "<secret>",
"authStrategies": ["jwt", "local", "system"],
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://yourdomain.com",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"name": "local",
"usernameField": "email",
"passwordField": "password"
},
"system": {
"name": "system",
"entity": "system",
"service": "systems",
"usernameField": "sysUsername",
"passwordField": "sysPassword"
}
},
"mongodb": "<url>"
}
Module versions:
"dependencies": {
"@feathersjs/authentication": "^4.3.10",
"@feathersjs/authentication-local": "^4.3.10",
"@feathersjs/authentication-oauth": "^4.3.10",
"@feathersjs/configuration": "^4.3.10",
"@feathersjs/errors": "^4.3.10",
"@feathersjs/express": "^4.3.10",
"@feathersjs/feathers": "^4.3.10",
"@feathersjs/socketio": "^4.3.10",
"@feathersjs/transport-commons": "^4.3.10",
"compression": "^1.7.4",
"cors": "^2.8.5",
"feathers-mongoose": "^8.1.0",
"helmet": "^3.21.2",
"mongodb-core": "^3.2.7",
"mongoose": "^5.7.8",
"serve-favicon": "^2.5.0",
"winston": "^3.2.1"
},
"devDependencies": {
"axios": "^0.19.0",
"eslint": "^6.6.0",
"mocha": "^6.2.2",
"nodemon": "^1.19.4"
}
NodeJS version:v12.13.0 Operating System:Linux 18.04
Did you register the system strategy as another localStrategy instance?
authService.register('system', new LocalStrategy());
Thank you @daffl. My system client (which is basically a nodejs application) is now able to login with credentials local strategy. But failing to authenticate with jwt token
server authentication.js
const { AuthenticationService, JWTStrategy } = require("@feathersjs/authentication");
const { LocalStrategy } = require("@feathersjs/authentication-local");
const { expressOauth } = require("@feathersjs/authentication-oauth");
module.exports = app => {
const authentication = new AuthenticationService(app, "authentication");
authentication.register("jwt", new JWTStrategy());
authentication.register("local", new LocalStrategy());
authentication.register("system", new LocalStrategy());
app.use("/authentication", authentication);
app.configure(expressOauth());
};
client authenticate jwt
app
.authenticate({
strategy: "jwt",
accessToken
})
.then(res => {
console.log("System Logged in via token!");
})
.catch(err => {
console.log("JWT token is Invalid!", err);
});
producing the error below:
{ Error: An id must be provided to the 'get' method
at Object.validate (/<path>/node_modules/@feathersjs/feathers/lib/hooks/base.js:23:11)
at /<path>/node_modules/@feathersjs/commons/lib/hooks.js:115:46
at processTicksAndRejections (internal/process/task_queues.js:93:5)
hook:
{ type: 'before',
arguments: [Array],
service: [Object],
app: [Object],
method: 'create',
path: 'authentication',
data: [Object],
params: {} } } }
Excuse my newbie terminology here, I do realize that I need to somehow direct/reference jwt authentication to a user or a system, which is something I need to study.
I am not sure how to do that yet!
Suggesting an alternative approach
Do you reckon it is better (easier) to separate the authentication into two separate services as I did below:
default.json
"authentication": {
"entity": "user",
"service": "users",
"secret": "<secret>",
"authStrategies": ["jwt", "local"],
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://yourdomain.com",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"usernameField": "email",
"passwordField": "password"
}
},
"systems-authentication": {
"entity": "system",
"service": "systems",
"secret": "<secret>",
"authStrategies": ["jwt", "local"],
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://yourdomain.com",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"usernameField": "sysUsername",
"passwordField": "sysPassword"
}
},
Then registered them as follows
authentication.js
module.exports = app => {
const authentication = new AuthenticationService(app, "authentication");
const systemsAuthentication = new AuthenticationService(app, "systems-authentication");
authentication.register("jwt", new JWTStrategy());
authentication.register("local", new LocalStrategy());
systemsAuthentication.register("jwt", new JWTStrategy());
systemsAuthentication.register("local", new LocalStrategy());
app.use("/authentication", authentication);
app.use("/systems-authentication", systemsAuthentication);
app.configure(expressOauth());
};
I also had to change my systems.hooks.js from
authenticate("jwt")
to
authenticate({
service: "systems-authentication",
strategies: ["jwt"]
})
on client side I did the setup with custom authentication path systems-authentication
const feathers = require("@feathersjs/feathers");
const socketio = require("@feathersjs/socketio-client");
const io = require("socket.io-client");
const authentication = require("@feathersjs/authentication-client");
// this is the server url
const socket = io("<server-url>", { transports: ["websocket"], forceNew: true });
const cloudClient = feathers();
cloudClient.configure(socketio(socket));
cloudClient.configure(authentication({ path: "/systems-authentication" })); // < --- Here
With that I managed to successfully authenticate 'system' via both local and jwt strategies without issues.
Do you agree with this approach or am I over complicating things here?
Suggesting an alternative approach
Do you reckon it is better (easier) to separate the authentication into two separate services as I did below:
default.json"authentication": { "entity": "user", "service": "users", "secret": "<secret>", "authStrategies": ["jwt", "local"], "jwtOptions": { "header": { "typ": "access" }, "audience": "https://yourdomain.com", "issuer": "feathers", "algorithm": "HS256", "expiresIn": "1d" }, "local": { "usernameField": "email", "passwordField": "password" } }, "systems-authentication": { "entity": "system", "service": "systems", "secret": "<secret>", "authStrategies": ["jwt", "local"], "jwtOptions": { "header": { "typ": "access" }, "audience": "https://yourdomain.com", "issuer": "feathers", "algorithm": "HS256", "expiresIn": "1d" }, "local": { "usernameField": "sysUsername", "passwordField": "sysPassword" } },Then registered them as follows
authentication.jsmodule.exports = app => { const authentication = new AuthenticationService(app, "authentication"); const systemsAuthentication = new AuthenticationService(app, "systems-authentication"); authentication.register("jwt", new JWTStrategy()); authentication.register("local", new LocalStrategy()); systemsAuthentication.register("jwt", new JWTStrategy()); systemsAuthentication.register("local", new LocalStrategy()); app.use("/authentication", authentication); app.use("/systems-authentication", systemsAuthentication); app.configure(expressOauth()); };I also had to change my
systems.hooks.jsfromauthenticate("jwt")to
authenticate({ service: "systems-authentication", strategies: ["jwt"] })on client side I did the setup with custom authentication path
systems-authenticationconst feathers = require("@feathersjs/feathers"); const socketio = require("@feathersjs/socketio-client"); const io = require("socket.io-client"); const authentication = require("@feathersjs/authentication-client"); // this is the server url const socket = io("<server-url>", { transports: ["websocket"], forceNew: true }); const cloudClient = feathers(); cloudClient.configure(socketio(socket)); cloudClient.configure(authentication({ path: "/systems-authentication" })); // < --- HereWith that I managed to successfully authenticate 'system' via both
localandjwtstrategies without issues.Do you agree with this approach or am I over complicating things here?
How about if you want the systemAuth and the normal Auth to use a common service
For instance a normal auth user wants to use a message service and likewise the systemAuth user.
You may create a custom hook to check which entity is being used for auth:
// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html
import { Hook, HookContext } from '@feathersjs/feathers';
import { iff } from 'feathers-hooks-common';
import * as authentication from '@feathersjs/authentication';
const { authenticate } = authentication.hooks;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default (options = {}): Hook => {
return iff(
(context: HookContext): boolean => !!context.params.%YOUR-CUSTOM-AUTH-ENTITY%,
authenticate({
service: '%YOUR-CUSTOM-AUTH-SERVICE%-authentication',
strategies: ['jwt']
}),
authenticate('jwt')
);
};
and in service(s):
import multiAuthenticate from '../../hooks/multi-authenticate';
// Don't remove this comment. It's needed to format import lines nicely.
export default {
before: {
all: [
multiAuthenticate() // instead of authenticate('jwt')
],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};