two different login with node-passport - node.js

I have the same application for 2 sites, the front-site and the backoffice.Both sites require a different condition to login because the backoffice needs to have the is_admin value in "true".
this is my login method
module.exports.verifyCredentials = function (username, password, done) {
mongoose.model("User").findOne({password:password, username:username, is_admin:true}, function(err, user) {
done(err, user);
});
};
and this is in my app.js
passport.use(new passportLocal.Strategy(securityCtl.verifyCredentials));
app.post("/administracion/login", passport.authenticate('local'), securityCtl.doLogin);
The doLogin methods just makes a redirect.
how can I send the frontsite login and the backoffice login to different methods?
Thanks!

Just use different names in each strategy and refers to they by their names in the passport.authenticate method. Now you can specify different methods (verifyFrontSiteCredentials for front-site and verifyBackOfficeCredentials for backoffice respectively) in each strategy. Something like this:
app.js
// front-site strategy
passport.use('front-site', new passportLocal.Strategy(securityCtl.verifyFrontSiteCredentials));
app.post("/administracion/front-site/login", passport.authenticate('front-site'), securityCtl.doLogin);
// backoffice strategy
passport.use('backoffice', new passportLocal.Strategy(securityCtl.verifyBackOfficeCredentials));
app.post("/administracion/backoffice/login", passport.authenticate('backoffice'), securityCtl.doLogin);

Related

Associating user data between multiple passport strategies

I'm using node, express, express-session, mongoose and passport-discord to authenticate users. But I also wish to use passport-steam to optionally link a user's steam account to their discord account.
The problem is, in order to link their steam account, I would also need a way of knowing which discord user is trying to do so, but the authentication callback doesn't include any request/response headers to access the current session, and the database I'm storing discord users in doesn't have any steam data in it until steam's authentication callback is called, at which point I have no idea which discord user is trying to authenticate their steam account.
To help remedy this, I've setup 3 mongoose models to have 3 separate database collections:
DiscordUser (stores discord user information, including their discordId)
SteamUser (stores steam user information, including their steamId)
User (stores required discordId and optional steamId)
I then try to serialize and deserialize the User object, but the passport.deserializeUser(obj, done) method seems to pass in an id instead of a User object, and I can't be sure if it's a discordId or a steamId, or which discord user I'm deserializing a SteamUser for, so I end up back at square one.
What's the best way to go about associating these users?
If needed, I can supply some code snippets upon request.
I found an article which helped solve this problem.
Here's what I did to fix it:
Using passReqToCallback: true in the new Strategy(...) options
In both strategy callback functions, adding req as the first parameter(ie., async function callback(req, accessToken, refreshToken, profile, done) { ... })
And then changing this:
router.get('/steam', passport.authenticate('steam'), (req, res) => {
//...
});
To this:
// Note that the route here is the same as the callback/redirect
router.get('/steam/redirect', (req, res, next) => {
passport.authenticate('steam', (err, user, { nextRoute }) => {
if (err)
return next(err);
if (nextRoute) {
// user was authorized with provider?
// send em back to profile
return res.redirect(nextRoute);
} else {
// give em some cookies
req.logIn(user, err => {
if (err)
return next(err);
return res.redirect('/');
});
}
})(req, res, next)
});
And then finally, modifying the User model schema to include the sessionID, which can be accessed via req.sessionID (or req.session.sessionID) from within the callback functions of both strategies.
This allows me to update the database upon successful login with my primary strategy(s) (discord, in this case), and then reference it from secondary strategies (steam, in this case), providing enough time to update the User database to have linked information between each strategy for as long as the session is still valid. After which, you can always simply reference the User database anywhere in code to check for valid primary and secondary strategy ids, allowing you to cross-reference other database collections for information, update the session id if the current session id doesn't match (and possibly forcing the user to re-authenticate?), etc.

how can I do, if user logined, he can change only his profile nodejs/mongodb/mongoose/pug

So basically I have a dynamic route that routes to every user username in DB
router.get('/:username', function (req, res) {
User.findOne({username:decodeURI(req.params.username)}, function (err, user) {
if (err) throw err;
if (user == null) {
}else if(user !== null){
res.render('user-profile', {
user:user
});
}
});
});
And I want to check for example: if the user that logged in is "alpha", and he seeing his profile on this URL: http://localhost:8080/users/alpha, then display him private information so only he can see it.
And if "beta" wanna see "alpha" profile, he doesn't see any private information of alpha
I'm working with passport, passport-local and express session
It is not as Complicated as you think . So, Lets discuss the scenario There are two users A and B . What we want to achieve here is that the url /users/:username only displays the public information of the user and if username is same as that of the logged-in user in that case it would also display the private information of the logged in User
A very simple approach would be to list all the information (public and private) but only display the private information on the profile page if the id or username for /users/:username is same as the logged in user.(can easily be acheived through any template engine)
if you are using passport then the logged in users info gets attached to the req object that you could use to simply check whether you need to display the information or not.

How to add current logged in status to Users Schema in MongoDB using Passport and node.js

I’m quite new to backend development…
With using my API I would like to be able to display a list of users and also indicate if they are currently logged in. I got the basic authentification working using passport and json web token
I’m not looking to get the current logged in user.
I want to be able to retrieve a list of users and see if they are logged in or not.
Like this:
var users = Users.find({});
// console.log(users) output:
{
name: 'foo'
password: ...
isLoggedIn: false
},
{
name: 'bar'
password: ...
isLoggedIn: true
},
{
name: 'baz'
password: ...
isLoggedIn: false
}
isLoggedIn would be set to true if the user is currently logged in and to falseif not.
How can I do that? Thank you!
It sounds like what you would like to do is update your MongoDB database based on login/logout events. To do this you could use something like mongoose to work with your Node backend to easily access your database in MongoDB.
You can include mongoose after installing with npm install mongoose like so:
var mongoose = require('mongoose');
var User = mongoose.model('User');
Note that User corresponds to whatever schema you create for storing user information.
Assuming you have some sort of router object for handling requests, you could construct route handlers for /logout and /login and use your imported mongoose User model to retrieve and then modify a specific User object as such:
// whenever user goes to '/login' (you can have, say, your 'login' button make a request to this URL
router.get('/login', function(req,res) {
// your authentication here; passport stores the currently authenticated user in req.user
var username = req.user.name; // here we assume the username is stored as 'name' as you have in your code but change this based on your schema
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = true;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to some page here maybe
}
});
});
});
// whenever user goes to '/logout' (you can have a logout button make a request to this URL
router.get('/logout', function(req,res) {
// currently authenticated user is still in req.user
var username = req.user.name;
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = false;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to login/register page maybe
}
});
});
});
So to summarize what this code would do:
based on the url a user would go to, our route handler would fetch one correct, unique User object from our database based on the name (username)
it would do so by accessing the username property of req.user which corresponds to the currently authenticated user with Passport, which, again will be different for all users
update the field that we use to keep track of login status (isLoggedIn)
and then save the changes, after which we are done updating the state to reflect whether the user is logged in or not, so we can now redirect to some other page or display other content
Finally then, you could retrieve a list of all users similarly to your code like so:
User.find({}, function(err, users, data) {
// all users from your database are in `users`
console.log(users);
});
Edit for expired sessions:
So, to track expired sessions, since you're using Passport, would in theory require functionality to signal with some sort of event / callback / message, etc. the moment the session is deemed invalid. Now that is tough to monitor and from my experience with Passport, stuff like that isn't implemented in all authentication strategies and might vary based on the strategy to be used by developers (think for instance if a browser window is closed, based on Passports authentication strategy, or just browser, it might destroy the cookie for the session right away and our server has no way of knowing about it). I do recommend checking out all the authentication strategies Passport offers in case there are some better ones here.
Now, if you would like to add functionality to track the users passive login/logout status with sessions yourself, you could use something related to cookies. Again, not necessarily one to use, but here's a couple handy Express modules: cookie-parser and cookie-session.
Then, you could set and read cookies like this, using cookie-parser:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
You would put this code somewhere right after the user is authenticated:
// cookies are stored here
console.log(req.cookies);
// configure your cookie
var options = {
expires: 1000 * 60 * 60, // expires after one hour
httpOnly: true
}
// Set cookie
res.cookie('session', ('user-' + req.user.name), options);
And then, on the client side check if that cookie is valid continuously on some time interval, and if it expired Date.now() > cookie.expires then make a GET request to /logout, and there log out the user (currently still authenticated) by updating MongoDB and all.
However, since this would require making a mechanism to basically simulate an expired session, I would recommend using something analogous to a timeout, which would be much easier to implement. Just a note, this is sort of analogous to mechanisms on some pages you might have encountered where you get a pop-up saying 'You will be logged out due to inactivity'. In your main.js or whatever client-side script define a function to keep going on a time-out, unless the user does some action.
var inactivity = function () {
var t;
// user doing something on your page, so keep resetting time counter when events happen
document.onmousemove = resetTimer;
document.onkeypress = resetTimer;
// this is a callback function that will get called once a time-out countdown is done
function timeOut() {
// make a request to '/logout' here and logout the current user (you still will have access to req.user from Passport)
// also can redirect from back-end route handler to the login page for instance
}
// this gets called whenever an event happens, resetting the counter of sorts
function resetTimer() {
t = 0;
t = setTimeout(timeOut, 1000 * 60 ) // set this to however long you should wait to log out your user time (in milliseconds)
}
};
So basically what this approach would let you do, is automatically invalidate sessions yourself, which means you would have much greater control over updating the state of your database and logging users out.
Hope this helps!

implementing a logout functionality in passport-saml using Http-post method

We have implemented a SAML SSO ,we have used passport-saml for the same. The login works perfectly using "http-post" as authnRequestBinding.But I am unable to find any such method for logout . it appears logout defaults to http redirection and this does not work correctly.
First of all, you need to define the logoutUrl and logoutCallback in the config for the passport-saml. The logoutUrl is the url where your server will send the logoutRequest. So it is an URL got from the identity provider you are using. The logoutCallback is as the name says, the callbackUrl which will be called by the browser (through redirect) after logout is done. Here's how you add those configurations:
module.exports = {
development: {
app: {
name: 'Passport SAML strategy example',
port: process.env.PORT || 8443
},
passport: {
strategy: 'saml',
saml: {
logoutUrl: 'idp-logout-url-here',
logoutCallback: 'your-logout-callback-url-here'
}
}
}
};
Then you need to have the SamlStrategy somewhere in your code, where you will use the config defined above. Of course the config will have other variables as well, I just put the logout related variables there for now.
Finally, you need to have your own logout route defined in your node application, which will initiate the logout process when called:
app.get('/logout', function(req, res) {
if (req.user == null) {
return res.redirect('/');
}
return SamlStrategy.logout(req, function(err, uri) {
return res.redirect(uri);
});
});
As you can see from above, it will call the logout function defined in the SamlStrategy. So there is a logout function defined in the passport-saml strategy. As in the above, you need to give it a callback function, which will then redirect the response to the uri. That uri will be the logoutCallback url you defined earlier.
If you're wondering what is the SamlStrategy there, it is actually the strategy of the passport-saml. I can show you how to get it working. In a separate file, called 'saml-strategy.js' for example, put this:
const SamlStrategy = require('passport-saml').Strategy;
var config = require('./config.js')['development'];
module.exports = new SamlStrategy(
{
otherImportantvariable1: config.passport.saml.OtherImportantvariable1,
logoutUrl: config.passport.saml.logoutUrl,
logoutCallback: config.passport.saml.logoutCallback
}
function (profile, done) {
user = Object.assign({}, profile);
return done(null, user);
}
);
Insert all your important config variables same way as the logout related variables are defined above. Include the config created in the first step.
Then you can just require the SamlStrategy to the same file where you have your routes:
const SamlStrategy = require('../config/saml-strategy');
Please ask if anything is unclear!
Logout fix for ADFS is as follows,
1) Session index attribute should be added as part of passport-saml logout request. you can get that from passport profile object.
function (profile, done) {
console.log('Profile: %j', profile);
return done(null,
{
id: profile.nameID,
sessionIndex: profile.sessionIndex
});
If there is no sessionIndex returned from ADFS. Then, NameID rule in relying part trusts should be as follows,
Add NameID as "Claim rule name", choose "Active Directory" as Attribute store, choose "SAM-Account-Name" as LDAP Attribute and "Name ID" as "Outgoing claim type", finish the wizard and confirm the claim rules window. (Reference: spring saml ADFS)
2) Debug using the ADFS logs (Event viewer) and check whether your error is similar to the one below,
The SAML Single Logout request does not correspond to the logged-in
session participant. Requestor: app.yyy.com
Request name identifier:
Format: urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified,
NameQualifier: SPNameQualifier: , SPProvidedId: Logged-in session
participants: Count: 1, [Issuer: app.yyy.com, NameID: (Format: ,
NameQualifier: SPNameQualifier: , SPProvidedId: )]
This request failed.
If yes, It means that nameIDFormat should be left empty for logging out the session. Previously I was using nameIDFormat that was specified for authentication request and it did not work.
HTTP-POST binding does not make any difference without this config.
Hope this helps !

bind error with ldap authentication using Passport and node.js

I have problem to bind to the ldap server. I have following codes:
in passport.js
module.exports = function() {
// Serialize sessions
passport.serializeUser(function(user, done) {
console.log('userid' + user.id)
done(null, user.id);
});
// Deserialize sessions
passport.deserializeUser(function(id, done) {
User.findOne({
_id: id
}, '-password', function(err, user) {
done(err, user);
});
});
passport.use(new LdapStrategy({
usernameField:'username',
passwordField:'password',
server: {
url: 'ldap://xxx',
//bindDn: 'AD\\'+'username',
searchFilter: '(objectclass=*)',
searchBase: 'ou=rzuser, dc=xxx, dc=xxx, dc=xxx',
}
},
return done(null, user);
}
));
};
I have authenticated the ldap strategy in server side with following code:
passport.authenticate('ldapauth', function(err, user, info) {
even with correct username and password, I get following error:
[OperationsError: 000004DC: LdapErr: DSID-0C0906DD, comment: In order to
perform this operation a successful bind must be completed on the
connection., data 0, v1772].
I think the problem is passing the correct username to the server. My ldap server accepts username with domain name as username: domain\username. In my case domain is "AD". so the passed username should be "AD\username". Can any body help me in using correct configurations to pass this to the server?
passport-ldapauth (disclaimer: I'm the author) does the following:
Bind to the LDAP server using bindDn and bindCredentials, if provided
Search for the user over this admin connection using the defined searchFilter and searchBase
If one, and only one, result is returned, attempt to bind using that result and the user given password.
You are not passing the admin credentials, ie. you're trying to do anonymous search, and that would be a probable cause for the error if the server does not allow anonymous access (which is probably the most common scenario). You should define bindDn (use full DN to be safe) and bindCredentials. Usually a service account is used, ie. something that is not anyones personal account.
Step 3 is done because LDAP servers often require full DN to bind, but even if users knew their DN it is not very convenient username for login. This also applies to the bindDn, although some servers do allow using some other form, eg. email address, directly.
Login will still fail unless there is only one use because your search filter will return every object from LDAP, and step 3 will not be performed. You will need to use the username provided by the user logging in in the search filter. For example, (samaccountname={{username}}) would search for a user whose username is the one provided by the user trying to log in.

Resources