Feathersjs local auth password verification doesn't work - passport.js

I've set the fields:
local: { usernameField: 'username', passwordField: 'sha_pass_hash' }
and the model like it is supposed to be. Whenever i try to send my payload to the server i only receive error: (401) Route: /auth/local - Invalid login.
Now my question is how the password verification works, since i, by no chance, can get this working.
The Password is stored as a sha1 inside the database and i sent the sha1 password as part of the payload.
Why is it not comparing them directly, what exactly does it? I'm just confused.

The default hash comparison is expecting a bcrypt hashed (+ salted) string (created by the hashPassword hook).
If you want to use plain SHA1 hashed passwords you can use auth 1.0 and implement a custom verifier.

Related

Node.js. Encrypted vs Signed cookies

I want to retrieve values from my cookie.
I am using passport.js, and In this quest, I have tried different things.
I run req.cookies; which gives me this:
's:x05d6V5Dhf6efFGjIkO26Ka1.imN4lT7OhW83Nc9Z7vw5dFbCoQhanP3aa37iyNWQyvU' }
Is this a signed cookie or an encrypted cookie?
The 's:' in the beginning makes it seem like a signed one, however when I ran req.signedCookies;, I got back an empty object {}. So is this cookie signed or encrypted?
I then ran: req.sessionID; which gave back this:
x05d6V5Dhf6efFGjIkO26Ka1
As you can see, this is also contained in my cookie, before the dot. So I guess the sessionId is stored in my cookie, right?
Then I ran req.secret; which in turn returned undefined.
Is the secret in the session, which is declared here:
app.use(express.session({ secret: 'blablablabla' }));
used to sign the cookie or to encrypt it?
The cookie-signature module can only unsign a cookie and not decrypt them - is that correct?
It can unsign it based on the previous session secret?
Finally, where is this cookie stored? In my mongo db?
The part after the dot:
imN4lT7OhW83Nc9Z7vw5dFbCoQhanP3aa37iyNWQyvU
is the signature of the session id:
x05d6V5Dhf6efFGjIkO26Ka1
That is to say, the session id is encrypted with the secret and append to the session id with a dot to compose the cookie.
The secret is not contained in the cookie.
The cookie-signature module can unsign a cookie if you provided the right info:
cookie-signature.ungisn(`${the_original_sessionid}.${the_encypted_sessionid}`,secret)

How to properly implement serializeUser with passport.js?

Okay, so I am just getting in to the MEAN stack, and I'm trying to build an app with Passport.js.
I'm just starting user serialization to maintain sessions. In their example, Passport uses this for serialization and deserialization:
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
So, my question is this: is this example considered secure? If I understand this right, doesn't that mean that a client could just fake the user ID to become logged in as whichever user has that ID?
I guess what I'm asking is, is their example considered "secure" and a proper way of doing things, or is it expected that you will change these functions to generate unique serialization. If this is considered secure, then I think I'm missing something on how this works, and I'd love to be filled in.
On the other hand, if this is not secure and I am expected to write my own functions in place of these, would the following be a valid and secure way of doing this:
Upon serialization of a user, generate a random hash and put that in the user's database entry. Random hash is the serial that represents that user.
Upon deserialization, look up the random hash in the database and return the corresponding user. If the hash isn't found throw some kind of error.
When the user logs out, delete their serial hash from their entry in the database.
If my logic up until here is valid, what would be a proper way to generate this random hash?
Yes, that is how you do serialization / deserialization. The id is not received from the client.
Session information is stored to your local session store, eg. database, under a random ID. For example, express-session uses uid-safe to generate the session ID. This ID is set to a cookie which is then sent to the client.
When the client makes a request, the session ID is read from the cookie if it has not been tampered (usually the ID is signed with the secret you define when intializing sessions). Using this ID, the real session data is read from your local session store. This is where the id used in deserialization comes from.
Here is an example what a session object stored to MongoDB could look like:
{
"_id" : "_RXnIfFeb_qH6AXMO2ounrxlJZPHkwda",
"session" : "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":true,\"path\":\"/\"},\"passport\":{\"user\":\"5614c62e4372842244660dcf\"}}"
}
The _id here is what is signed and sent in the cookie. The session string, decoded to JSON object, is:
{
"cookie": {
"originalMaxAge": null,
"expires": null,
"secure": false,
"httpOnly": true,
"path": "/"
},
"passport": {
"user": "5614c62e4372842244660dcf"
}
}
Here, passport.user is the actual user ID returned by my applications seralizeUser that is given to deserializeUser when a session is being loaded.
So what happens if you change the cookie content? If the cookie is signed, it will be invalidated because the altered value does not match the signature. If it is not signed, the value is used when querying session store. The query will not return anything because you changed the ID and there is no matching session in the database (unless you have found out / guessed the session ID of another active session - ie. performing session hijacking).

NodeJS - Passport password appears as plain text in DB

Probably I am missing something here.
Got an Express server with MongoDB and i'm using passport to authenticate.
I'm using one of the standard code example to signup and it seems ok, but I can see the password I type in the password field (plain text) in my DB.
I expected it to be encrypted...
Am i doing something wrong?
You have to hash the password yourself. Here is how to do it using brcypt:
function hashPassword (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync());
}
So before you save your user to the DB simply invoke that function like so:
user.password = hashPassword(thepassword);

Nodejs - Is it necessary to decode an Authentication Token?

I am using a combination of Node.js and Passportjs on the server-side, and Emberjs on the client side for an app. My current Authentication strategy is to use Passport-Local to authenticate users with normal email/password combinations as standard, and then hook in a session creation mechanism to generate an Authentication Token, which is saved into a separate table, and passed back to the user for use in any further protected routes. (passed in the REST header).
Creation of the token is fine, i'm doing that without issue, however i'm struggling to work out if I need an extra step.
Currently, I generate the token with node-jwt-simple by using a random node-uuid pass as the payload, and the users UID (another node-uuid) as the secret. I then save this to a $.cookie on the clientside, and to a table on the serverside, along with a creation date.
Obviously, one of the steps in node-jwt-simple is to encode the token. There is also a decode function provided. My question is, do I need to decode the token into something when I am doing my auth checking, or is simply checking the user's session cookie (REST header) for a match against the token in the database sufficient? I wouldn't want to go to all the effort of having generated a token, only to then miss an important step, but i'm not seeing how I could decode it into anything that would provide any additional useful security.
UPDATE:
I think I worked this out last night:
The solution seems to be to use the User's UID as the payload for JWT, with a static string as the secret (taken from something like a server environment variable or similar), and then only store the encoded token in the database. Pass the token back to the client for re-auth, then when the client attempts to access a protected route, they must pass their UID along with the encoded token to the server, which is then decoded, and the decoded payload compared to the UID that has been passed. If they match, the auth is successful, otherwise the token is destroyed and the user has to log in again.
By doing this, it makes the store of tokens effectively useless without knowing either the Secret key, or having the User's UID, but makes the auth process more secure.
If you don't validate the token, you could as well create some other random data to use as a session cookie, as long as it is unique and cannot be guessed by clients.
But as you already made a lot of effort, you could encode something useful in the token which tells you how long it is valid, e.g. an exp field, so you don't have to read from the database.
I'm not sure if I fully understand your JWT, but the problem I see is that you need information to decode the token which is probably not at your hand. So you have to do a search in your database.
I think it would be sufficient to use some random session key, e.g. following function:
var crypto = require('crypto');
/**
* Create random bytes and encode base64url.
* #param {int} [lengthInBytes=40] the size of the raw token in bytes
* (will be longer since base64url-encoded)
* #param {function} callback node-style callback-function;
* data-parameter is a string w/ a shortened (no trailing ==)
* base64url-encoded string of the generated bytes.
*/
exports.createRandomToken = function createRandomToken(lengthInBytes, callback) {
if (typeof lengthInBytes === 'function') {
callback = lengthInBytes;
lengthInBytes = 40;
}
crypto.randomBytes(lengthInBytes, function (ex, buf) {
if (ex) {
callback(ex);
return;
}
callback(null, buf.toString('base64')
.replace(/\//g, '_')
.replace(/\+/g, '-')
.replace(/=/g, ''));
});
};
Set expire time of the token as small as possible(5 min, 30 min, nor months neither years).
Use refresh token to get a new token and update refresh token every time you update old token (and when user is logged in, no doubt)
Do not store passwords, credit card numbers and any confidential informations in the token ( I'm shure, you know it :) )
Store all necessary information for checking privileges ( or checking ip, for example ). It is good for REST API and horizontal scaling.

Re-validating expired AccessToken (or get new one) when logging into app via Facebook Strategy using Passport

Under a variety of circumstances, Facebook's internal AccessToken (used for various features in the Facebook API and other cases) may become invalid. For example, if a user changes her password between sessions of using an application that relies on Facebook Login. There are a few other cases as well.
What happens, however, is that your application essentially crashes, with an error like this: data={"error":{"message":"Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.", "type":"OAuthException", "code":190, "error_subcode":460}}.
Passport's Facebook Strategy uses the AccessToken, and it's available to an app as soon as a user is logged in / authenticated via Passport. The problem, however, is what to do when the above error is encountered. Facebook gives a convoluted re-auth flow as an example in PHP, but the general sense is that you need to re-authorize your app with Facebook.
The question is, even when removing the Facebook app from your Facebook page, and forcing the application relying on Facebook Login to re-authorize itself, it seems that Passport + Facebook Strategy is still picking up the last existing AccessToken from the browser's session storage. (At least that's what I'm seeing with Mozilla/Fx 26). I have re-authorized my app several times, but when debugging and looking at what Passport returns, I always get back the same invalid AccessToken. Seems like if an AccessToken exists in session-storage, Passport picks that up instead of getting a new one from Facebook.
So is there a way - within Passport + Facebook Strategy - to essentially ignore or override any stored AccessToken and always request a new one from Facebook, in the event of this kind of error? Not at all clear how to make that happen. Thanks for the help.
Update: The sample code for invoking the strategy has refreshToken as a parameter; what does this do? Is there a possible solution with this parameter?
passport.use(new FacebookStrategy(
{
...
},
function(accessToken, refreshToken, profile, done)
{
I found the answer to my question, and will leave it up in case anyone else encounters the same issue.
Passport, in its VerifyCallback function, passes the AccessToken to the application. The recommendation (tacit or otherwise) is to of course save this AccessToken as part of the user's persisted record (say, a MongoDB document). It'll be needed when making subsequent calls to the Facebook API, and though it can probably be safely retrieved from the request.user object passed around through the Express middleware, having your own local copy probably makes sense.
Now, when you're done authenticating the user via Passport, you still will pull up your user's persisted record to get other user data. Passport documentation features a generic function called Users.findOrCreate() to serve as a model/prototype of this function for you to implement. And here's where the issue lies: If you find a user (one already exists) via this function, you'll be using that record throughout your app. And if that record also holds an AccessToken, and that AccessToken has changed, then of course you'll be in trouble: Passport/Facebook is passing you a new AccessToken, but you're using an outdated one.
So the simple fix is this: If you've implemented a Users.findOrCreate() function to persist a user and rely on this to retrieve a user's set of data - and that record also stores the AccessToken - then make sure you check the current (old) AccessToken to the one Passport/Facebook is passing you in the VerifyCallback when a user has just been authenticated; if the two are different, update the user's persisted record and use this new Token.

Resources