We are developing a REST service but we already have an infrastructure in place to manage users. But we want to leverage the authentication and authorization mechanism of Loopback. The requirement is to
Add a remote method and receive the user credentials
Manually verify the credentials through stored procedure call
Generate the access token through Loopback
Going forward use Loopback authorization mechanisms such as roles in the application
Should I be implementing a custom login service provider using Loopback's third party login support ? I couldn't find a very good resource on this area. Any pointers would be much appreciated.
Please check some of the following examples to see if it fits your use case:
https://github.com/strongloop/loopback-example-access-control
https://github.com/strongloop/loopback-example-passport
My example is using a bootscript in express but you could easily change it into a remote method.
module.exports = function(app) {
//get User model from the express app
var UserModel = app.models.User;
app.post('/login', function(req, res) {
console.log(req.body);
//parse user credentials from request body
const userCredentials = {
"username": req.body.username,
"password": req.body.password
}
UserModel.findOne({
"where": {
"username": userCredentials.username
}
}, function(err, user) {
// Custom Login - Put the stored procedure call here
if (err) {
//custom logger
console.error(err);
res.status(401).json({
"error": "login failed"
});
return;
}
// Create the accesstoken and return the Token
user.createAccessToken(5000, function(err, token) {
console.log(token)
res.json({
"token": result.id,
"ttl": result.ttl
});
})
})
});
}
Now you can use that Token for Loopbacks authorization mechanism.
Related
I have a NodeJS API server using express and passport for authentication. I am using the passport azure ad bearer strategy for authentication with Microsoft Azure AD. In the examples provided in the documentation(https://github.com/Azure-Samples/active-directory-node-webapi/blob/master/node-server/app.js#L50), the owner (currentUser) is a javascript variable defined globally. How do I attach the user to the request so that I can do something like
server.post('/user', passport.authenticate('oauth-bearer', {
session: false
}), function(req, res, next){
console.log(`I am the current user ${req.user}`);
});
From the example mentioned in the OIDC strategy, I have seen a deserializeUser and serializeUser function that would allow the user ID to be stored in the session, However, in the bearer strategy, we do not have any sessions maintained as authentication will be performed for every incoming request.
This may be a gap in my understanding. I have gone through the documentation for the individual libraries. Please help me if there is a way this can be done
Following is the sample code via which you can embedd the yser object to request object . Thus when you will do ${req.user} in post callback you will get the user object
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
var bearerStrategy = new BearerStrategy(options,
function(token, done) {
findById(token.oid, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
// "Auto-registration"
return done(null, token);
}
const owner = token.oid;
return done(null, user, token);
});
}
);
I have a big CMS built with Meteor that until now used basic-auth to login as we didn't have more than one editor. However, more people will start working with it, so I am trying to create a login functionality through LDAP so any authorised employee can login with their username/password
I tried to do my best given poor documentation of WebApp.connectHandlers and the lack of content integrating passport with Meteor. I based my LDAP login code on express examples (assuming WebApp.connectHandlers.use() was the Meteor equivalent of Express' app.use())
The login part works, that is to query and verify the user through LDAP.
However I cannot figure how to identify the logged-in user when they make a Meteor.call( ).
What I have in mind at the moment is to send a JWT token back to the authenticated user, store it in a cookie or something, and whenever a Meteor.call( ) is made, the client passes the token as a parameter. Then I would be able to identify the caller and can for example store the username in the database as the person who made a certain change.
Good to mention that I am trying to avoid using Meteor's accounts system as the requirement is to use LDAP without creating any extra tables (that's why the complication of using those several packages together).
Here's my code, with working login but no idea how to pass back the token to the user.
Maybe my whole JWT logic is wrong, I would appreciate any help/suggestions.
var basicAuth = require("basic-auth");
var passport = require("passport");
var bodyParser = require("body-parser");
var LdapStrategy = require("passport-ldapauth");
var jwt = require("jsonwebtoken");
// Example of the real LDAP config
var OPTS = {
server: {
url: "ldap://address:port",
bindDN: "admin",
bindCredentials: "adminPassword",
searchBase: "OU=Users,DC=example,DC=com",
searchFilter: "(&(objectClass=user)(sAMAccountName={{username}}))"
},
credentialsLookup: basicAuth
};
Meteor.startup(() => {
passport.use(new LdapStrategy(OPTS));
WebApp.connectHandlers.use(bodyParser.json());
WebApp.connectHandlers.use(bodyParser.urlencoded({ extended: false }));
WebApp.connectHandlers.use(passport.initialize());
WebApp.connectHandlers.use(
"/",
(req, res, next) => {
// This part before the else is to trigger a basic auth popup to enter username/password
let credentials = basicAuth(req);
if (!credentials) {
res.statusCode = 401;
res.setHeader("WWW-Authenticate", "Basic");
res.end("Access denied");
} else {
passport.authenticate(
"ldapauth",
{
session: false
},
function(err, user, info) {
if (err) {
return next(err);
}
if (user) {
var token = jwt.sign(
{ username: user.sAMAccountName },
"someSecretString"
);
console.log("token", token);
next();
}
}
)(req, res, next);
}
},
function(req, res) {
console.log("debug point#2");
res.send({ status: "ok" });
}
);
}
I have a stupid question with feathersjs auth hooks (or whatever). This is my code with comment:
app.get('/admin', function (req, res) {
// i'd like to check here if (req.connection.isAdmin) then render page
res.render('admin/admin.html', {
title: 'admin'
})
});
I can't find where i can implement user-auth hook to chek user for admin role. How can i do that?
You should be able to use the example that I posted in this other question: Feathers Js Restrict Access To Page on Server Side
In your case, you'll need to do some logic to see if the user is an administrator. By default, the JWT token that Feathers gives you will only contain the userId. You can retrieve the user record to check if the user is an administrator.
Here's an example of what you would put inside the jwt.verify block:
jwt.verify(token, secret, function(err, decoded) {
if (err) {
return res.status(401).send('You are not authorized to view that page.');
}
app.service('users')
.get(decoded.id) // Or _id if you're using MongoDB
.then(user => {
if (user.isAdmin) {
res.render('admin/admin.html', {
title: 'admin'
})
} else {
return res.status(401).send('You are not authorized to view that page.');
}
})
});
It will be possible in the next version of feathers-authentication to add values to the JWT on the server, so, for administrators, you could add an isAdmin property to the JWT payload. This will be fairly trivial to do once the pre-release version is published. Until then, the above is probably the best way to go.
I am trying to build a RESTful API using Node.js w/ Express. I am fairly new to the MEAN stack, and want to use best practices. The concept I'm having trouble grasping and implementing is the following:
Restricting routes like PUT and DELETE on a user object, to only allow requests from users who 'own' this object.
One possibility I've thought of:
Creating secret token for users that matches token in DB
So when creating a user I assign them a token, store this in the DB and attach it to their session data.
Then my middleware would look something like:
router.put('/api/users/:user_id', function(req, res, next) {
// already unclear how this token should be transfered
var token = req.headers['x-access-token'] || req.session.token;
// update user (PUT /api/users/:user_id)
User.findById(req.params.user_id, function(err, user) {
if (err) {
res.send(err);
} else if (user.token != token) {
res.json({ sucess: false, message: 'User not same as authenticated user.' });
} else {
// set new information only if present in request
if (req.body.name) user.name = req.body.name;
if (req.body.username) user.username = req.body.username;
...
// save user
user.save(function(err) {
if (err) res.send(err);
// return message
res.json({ message: 'User updated.' });
});
}
});
Questions I have regarding best practice
Is the scenario I thought of at all plausible?
What data should I use to create a unique token for a user?
Is storing the token in the session the best solution?
Sidenote
This is a learning project for me, and I am aware of libraries like Passport.js. I want to learn the fundamentals first.
I have a repo for this project if you need to see some of the surrounding code I'm using: https://github.com/messerli90/node-api-ownership
Edit
I would accept a good RESTful API book recommendation, where these points are covered, as an answer.
Edit 2
I actually found a lot of the answers I was looking for in this tutorial: http://scottksmith.com/blog/2014/05/29/beer-locker-building-a-restful-api-with-node-passport/
I was trying to do this without the use of passport.js but a lot of the concepts covered in the article made some of the mechanics of an authorized API clear to me.
If I understand your question, this is an API, and the client (not a browser) is passing the secret token (api key) in the request, in a header. Seems reasonable. Of course, you must require https to protect the api key. And, you should have a way for users to revoke/regenerate their API key.
So far, I don't think you need to store anything in the session. It seems like storing the token in the session just complicates things. Presumably, if you are going to establish a session, the client has to include the token in the first request. So, why not just require it on each request and forget the session? I think this makes life simpler for the api client.
A 'bit' too late, but if someone is still looking for an answer, here is how i did it:
router.put('/', function(req, res) {
var token = req.headers['x-access-token'];
if (!token) return res.status(401).send({auth:false, message:'No token provided'});
jwt.verify (token, process.env.SECRET, function (err, decoded) {
if(err) return res.status(500).send({auth:false, message:'failed to auth token'});
User.findByIdAndUpdate({_id: decoded.user_id}, req.body, function(err, user) {
if (err)
res.send(err);
res.json({username: user.username, email: user.email});
});
});
});
Just pass the user id that is stored in the token to the mongoose function. This way the user who sent the request can only update or delete the model with his ID.
Reading material:
Implementing Access Control in Node.JS
Found this super clear article on how to allow users to only delete replies they own. Hope it helps.
What worked for me:
.delete(requireAuth, async (req, res, next) => {
const knexInstance = req.app.get("db");
const comment = await CommentsService.getById(knexInstance, req.params.id);
if (comment === undefined) {
return res.status(404).json({
error: {
message: `Comment doesn't exist.`
},
});
}
if (comment.users_id !== req.users.id) {
return res.status(401).json({
error: {
message: `You can only delete your own comments.`
},
});
}
CommentsService.deleteComment(knexInstance, req.params.id)
.then((numRowsAffected) => {
res.status(204).end();
})
.catch(next);
})
I want to access the Gmail API using NodeJS.
I'm using a server-to-server approach (see this) but when I execute the code below, I get a backEndError, code 500 from the Google API.
Any ideas?
var authClient = new google.auth.JWT(
'email',
'key.pem',
// Contents of private_key.pem if you want to load the pem file yourself
// (do not use the path parameter above if using this param)
'key',
// Scopes can be specified either as an array or as a single, space-delimited string
['https://www.googleapis.com/auth/gmail.readonly']
);
authClient.authorize(function(err, tokens) {
if (err)
console.log(err);
gmail.users.messages.list({ userId: 'me', auth: authClient }, function(err, resp) {
// handle err and response
if (err) {
console.log(err);
});
Yes, I have the same problem. If I use the scope "https://mail.google.com", I get
403
{
"error" : "access_denied",
"error_description" : "Requested client not authorized."
}
And if I use the scope "https://mail.google.com/" (notice the / at the end), I get
403
'Forbidden'
It seems to be related to using JWT and service account.