Approach to modify the database without logging the user? - javascript

I have a React frontend with a Node + MySQL backend, I'm sending an email to an user with two buttons to accept or decline a quote. What I'm trying to achieve is to make the buttons in the email modify the database securely without the user having to log into his account. My idea is to have two routes, one that sends the email containing the buttons which will have a url to my website with the jwt token on its parameters, and another for verifying said token and making the changes to the db. Here's some pseudo-code:
app.post("/email-quote", async function (req, res) {
const payload = {
uid: req.body.user.id,
quoteId: req.body.quote.id,
accepted: // true for the accept button, false for the decline button
}
const secret = ?
const token = jwt.sign(payload, secret);
// ...
// Generate and send email with buttons containing the url + the token
});
When the user clicks one of the buttons, I re-direct him to my website and there I can extract the token and verify its validity:
app.get("/verify-email-quote/:token", async function (req, res) {
const decodedJwt = jwt.decode(req.params.token);
const secret = ?
const verifiedJwt = jwt.verify(req.params.token, secret);
if (verifiedJwt) {
// Make the changes to the db
}
});
I wasn't able to find any examples trying to achieve something similar on the web, so I have these questions:
Would a jwt token be a good approach to achieve this?
If yes, what secret should I use to create the token?
If no, what other solutions could I look into?

Yes, you can do it this way.
The secret does not matter. As long as the secret is secret
It doesn't need to be a jwt token. It can just be a normal token. The incentive to using jwt is that you can embed a payload into the token. For your case, it looks like it is exclusively for verification purposes. It doesn't matter in the grand scheme of things, but if you don't have jwt already implemented, there's no need to go through all that extra work just for this use case.

Related

ReactJS - What instead of JWT to keep session

For some time I have been writing an application based on this tutorial:
https://jasonwatmore.com/post/2019/04/06/react-jwt-authentication-tutorial-example
My application is already much more developed, it uses other rest API servers, etc. Currently, after positive authentication, a JWT token is sent to the client, which is stored in localStorage. But!
I need to add such functionality: the assumption is that only one user can use one account at a time.
There are two solutions:
a) when the user is logged in, he logs out in the previous place after logging in on the other device/browser.
b) when user trying log in another device/browser get a throw for example "Client already login"
So, From what I learned, I can't do it using JWT. My question is :
What can I use instead of JWT to handle the session because I think it's about?
Thanks.
EDIT : My authenticate funtion from server side :
async function authenticate({ username, password }) {
console.log(username + " " + password);
const user = await User.findOne({ username });
if (user && bcrypt.compareSync(password, user.hash)) {
const { hash, ...userWithoutHash } = user.toObject();
const token = jwt.sign({ sub: user.id }, config.secret, { expiresIn: 300 });
return {
...userWithoutHash,
token
};
}
}
You may use a more traditional session ID based authentication approach. When a user logs in, a session ID (something along the lines of a UUID) gets generated. Then, using the user ID as a key, the session ID gets stored in a hashmap on the server. In all subsequent login attempts, the session ID in the map would be overwritten for the same user.
I suggest kicking out any other session during subsequent login for usability reasons. Imagine logging into your application from some device, but suddenly getting pulled into a meeting somewhere. Now, if we don't kick out previous sessions during login, any attempt on your part to login from a different device would fail, and you would be forced to wait to be able to login.
Note that once the new session ID has been recorded in the hashmap, any access attempts by the first device using the old session ID would fail.
You can use JWT to handle the session itself.
But to handle multiple logins to a single account, you can for example add a locked attribute to your users table in your database, provided you have such thing in your architecture. This would allow you to lock your session.
This solution only makes sense if you want to totally lock your session until logout (and not be able to login from another device for instance). If that is not your intention, I recommend Tim Biegeleisen's answer.
Example:
users || id | name | locked
----- || -- + ---- + ------
|| 1 | John | 0
|| 2 | Bob | 0
Then, we could imagine your session handling to be as such (simple example for an Express app, but see it as pseudo code):
database.query("SELECT locked FROM users WHERE name = $1", [name])
.then((data) => {
if (data.locked === 1) {
res.status(403).json();
} else {
database.query("UPDATE users SET locked = 1 WHERE name = $1", [name])
.then(() => {
// Define token key and object for jwt
// ...
var token = jwt.sign(...);
res.status(200).cookie('token', token).json();
});
}
});
And then, when a user logs out or is logged out by an expired jwt token, you call another endpoint of your API and you set the locked attribute to 0.
Hope this helps!

How to authorize all users with different tokens using jwt in Node JS

I have a node.js app. I'm using Json Web Token for authorization. When I Login with a user who is in my database, it creates a token for the user. But I can use that token for another user and it works too again. I need to have different tokens for all users and I should not use one user's token for another user. (I dont have internet on my work pc so I cant write my codes on my computer here sorry about that)
Here is my verify-token.js (middleware):
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
const decodedToken = jwt.verify(token, "secret_key");
req.userData = decodedToken;
next();
} catch (error) {
return res.status(401).send({
message: "Auth failed"
});
}
};
And here is my login code (im creating the token here)
if password is true:
const token = jwt.sign(
{
email: user.email,
password: user.password
},
"secret_key",
{
expiresIn: "2h"
}
);
return res.status(200).send({ message: "success", token: token });
And in app.js:
const checkAuth = require('../middleware/checkauth');
router.get('/api/company',checkAuth,companyController.list);
I expect that one token should be for just one user, and for every login it should creates a new token for all users. Any suggestion?
You're including the email in your token body; why not also include the user ID as a claim (field)? When you verify the token, if it's successful, it will return the body to you, so then you'll know which user that token was made for and reject if it's not the one who's making the request.
To ensure two people don't use the same token at the same time, you can keep a list of every valid token when you generate it, and when a token expires or is revoked (for instance, when the user signs out or reports an imposter, if it gets that far) remove it from the list. During verification, if the token is not on the list, don't even bother decoding it, just reject it immediately.
If you give your tokens decently small expiration windows (I believe the recommendation is to make them last no longer than 1 hour), you shouldn't have to worry about such things much.
EDIT To clarify, you'll never have a way to know for sure that the person who gave you the token is who they claim to be. You only know if your server created the token and if the token is currently valid. If you really want to prevent replay attacks (that is, make absolutely sure there's no way for two people to use the same token at once), you'll need to generate a new token every time one is used. If you keep that whitelist that I mentioned above, this regeneration ensures every token becomes invalid as soon as it's used once.
You can also, to be EXTRA confident, include a jti claim in the token body; this is a field intended to be filled with a random unique value every time a token is generated, so that you can keep track of the jti's you've received and not allow the same one to come in more than once. It's about the same as just keeping track of the tokens, though.

How to store login information for an api which requires Username - Password for the next session

I'm working with an unofficial n26 API (https://github.com/PierrickP/n26/tree/develop). The api requires the email and password to login to the account.
I don't want the user to login every single time so I need to store these information somehow.
I can't find any way to get a session token or something for later use from the api. (maybe someone of you can find it??)
So my question: How do I store Email/Password for later use in a secure way?
const N26 = require('n26');
// Log into the account
const account = new N26('example#mail.com', 'password');
// Get the last 10 transactions
account.then(account => account.transactions({limit: 10}))
.then(transactions => {
// Do something with it
});
http://www.passportjs.org/ Check out this package.
things to consider:
- json web tokens with refresh token
- API-keys

How to go about creating temporary authentication for a website?

I'm new to authentication with websites, and I've been doing a lot of reading on the different kinds of user authentication (for example session vs token authentication) you can use. However, a lot of it seems more than what I need, and I'm not sure which ones will be suitable for my cause.
My idea is to generate temporary user accounts and passwords that will expire after the first use. I want this integrated with my website, so they have one chance to view restricted pages, after which they will not allowed access to those parts again (unless provided with new credentials).
Any direction in the right step will be appreciated.
Update: I'm using Javascript(Node) as my server side language
Session-based authentication is actually incredibly lightweight if you're using a Node backend, due to most (if not all) webserver libraries supporting "middleware", which modify requests before they hit your route functions. The Express-compatable middleware client-sessions is fantastic for this, and I used it previously in a project with great success. It adds a cookie on the first request a user makes to your site which identifies them, and if at some point they log in, you can flag that session as authenticated, store session information, and other data related to them specifically.
Assuming you want both login & logout, the simplest way would to be to use POSTs over HTTPS to login & logout routes. Inside of the resolution for the login route, you would simply "mark for deletion" inside whatever database you're working with.
An example might look like this:
var app = express();
function authenticate(user, pw){
//do your application specific login verification here
}
function deleteAccount(user){
//do your application specific user removal here
}
app.use(require("express-session")({
secret : "YOUR-SECRET-KEY-HERE"
cookieName : "Session"
//any other desired config options go here
})
app.post("/login", function(req, res){
var user = req.body.user;
var pw = req.body.pw;
req.Session.isAuthenticated = authenticate(user, pw)
if(req.Session.isAuthenticated){
markForDeletion(user, pw);
}
res.write("logged in as: " + user);
res.end();
});
app.post("/logout", function(req, res){
deleteAccount(req.Session.username);
req.Session.username = "";
req.Session.isAuthenticated = false;
res.write("logged out!");
res.end();
});

How can I impersonate another user with Passport.js in Node?

Using Passport.js in Node, is there a way for me to allow one user to impersonate another? eg. as an Administrator in the application, I want to be able to log in as another user, without knowing their password.
Most simply, I would be satisfied if I could change the serialized user data (user ID) so when deserializeUser is called it will just assume the identity of the alternate user. I've tried replacing the value at req._passport.session.user and the value at req.session.passport.user but the net effect is just that my session seems to become invalid and Passport logs me out.
Passport provides a req.logIn method in case you want to do the authentication manually. You can use it to login any user even regardless of authentication.
Here's how you can use it. Have the Admin login normally, who'll have an isAdmin flag set.
Then place a middleware before passport.authenticate in your login route. This will login the new user based only on their username, if the currently logged in user isAdmin.
app.post('/login',
function forceLogin(req, res, next) {
if (!req.user.isAdmin) return next(); // skip if not admin
User.findOne({
username: req.body.username // < entered username
}, function(err, user) {
// no checking for password
req.logIn(user);
res.redirect('/users/' + user.username);
});
},
passport.authenticate('local'),
function(req, res) {
res.redirect('/users/' + req.user.username);
}
);
I have another way to impersonate, because:
I didn't want to mess with internals of authentication/passport like
session storage / logIn / etc. You must understand them really well
and also they are prone to change so I'd say it's not an option for
me.
Also, I'd like to still be able to tell if action is made from
superuser (impersonated) or normal user (not impersonated).
What I do is:
Have a route for user with superadmin role to impersonate, like /superadmin/impersonate?username=normaluser1 which sets req.user.impersonated.userid = normaluser1.userid
Then I have a middleware, which checks if user is superadmin and is impersonated:
if (req.user.isAdmin && req.user.impersonated) {
req.user.userid = req.user.impersonated.userid;
}
Also, I have found this to be a good article about user impersonation. Similar to my approach, and good for inspiration for building something similar.
The answer to your question is basically: no. The reason is this: the sessions library that is being used 99% of the time is signing the cookies, so if you tamper with the data the web server will reject it.
The way around this is to write your own passport authentication strategy that obviously doesn't do this, but I'm assuming you're talking about working with the built-in strategies here.

Categories

Resources