Need assistance with the JWT authenticate process - node.js

I am working on a user login project. My front end is React, and my server side is Node/Express js. I am trying to learn about JWT to create protected routes. However, I have gotten stuck when it comes to sending my token (already successfully generated) back to my front end application. I am trying to store the token in a cookie, then send that back to my react side. It is my understanding that I must store the token on the react side before I can access a protected route on my server side, but I am at a loss as to how to proceed to accomplish this. Here is my code for the login post request on my server:
app.post('/userLogin', function(req, res) {
var {usernameLogin, passwordLogin} = req.query;
console.log(req.query);
var validateLogin = `SELECT CustomerID FROM CUSTOMER WHERE Username='${usernameLogin}' AND Password='${passwordLogin}'`;
mysqlConnection.query(validateLogin, (error, result) => {
if(error) {
console.log(error);
} else {
// Checks to see if the result object (CustomerID) exists or not.
if(Object.entries(result).length === 0) {
console.log('sorry');
} else {
console.log(result);
console.log('existing');
const token = jwt.sign({ usernameLogin: usernameLogin}, 'loginKey');
res.cookie('token', token, { httpOnly: true}).sendStatus(200);
console.log(token);
}
}
});
});
and this is my react onSubmit (called login) in my react app. Just as a side note, the "it worked" message successfully is printed to the console, but this is the point at which I do not know how to retrieve and store the token on my client side for future use to access protected routes:
login = (event) => {
event.preventDefault();
console.log(this.state);
fetch('http://localhost:3001/userLogin?usernameLogin=' + this.state.usernameLogin +
'&passwordLogin=' + this.state.passwordLogin, {method: 'POST'}
)
.then(res => {
if (res.status === 200) {
console.log("it worked!!!");
} else {
console.log('there was a problem at line 27');
}
})
};
Any insight would be greatly appreciated!!!

For the client side, you can store the token returned from server side using localStorage property.
Example to set the item: localStorage.setItem("token", <token>);
Example to get the item: let token = localStorage.getItem("token");
Docs: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Local_storage
PS: Let me know if was enough to solve your question.

Related

NodeJS browser session

I'm quite new to NodeJS so could someone explain this to me:
I've got a simple authorization on my endpoint created in express. As you can see in the code there is no JWT or session cookie set anywhere. I've also checked in browses cookies just to be sure and there is no sessionId set anywhere.
With that in mind I'd expect my app to prompt for credentials each time I refresh the page ( or at least when I manually delete all cookies, refresh cache and then refresh the page) but that's not happening. Browser is still sending request with correct authorization header (after i put credentials for the first time and refresh page) and I have no idea why.
simple endpoint code:
app.get('/cms', function (req, res) {
const reject = () => {
res.setHeader("www-authenticate", "Basic");
res.sendStatus(401);
};
const authorization = req.headers.authorization;
if (!authorization) {
return reject();
}
const [username, password] = Buffer.from(
authorization.replace("Basic ", ""),
"base64"
)
.toString()
.split(":");
if (!(username === "user" && password === "pass")) {
return reject();
}
res.status(200)
res.sendFile(path.join(__dirname+'/cms.html'));
});

Destroy JsonWebToken on logout request in Node.js

I want to destroy the JWT whenever user sends the logout request to the app.
First of all, I am not storing JWT in the database so I can not delete that and I am also not using cookies or sessions. Is JWT stored on the client side? If so how can I destroy the JWT and invalidate the user's requests after logging out of the app.
The token middleware:
module.exports = middlewares = {
authenticateToken: async (req, res, next) => {
try {
if (!req.headers["x-access-token"]) {
return res.status(401).json({
error: "Key x-access-token not found",
});
}
if (req.headers["x-access-token"] === "") {
return res.status(401).json({
error: "Token not found",
});
}
const token = req.headers["x-access-token"];
const data = jwt.verify(token, keys.JWToken);
if (!data) return res.status(401).json({ error: "Invalid token" });
req.data = data;
next();
} catch (err) {
return res.status(400).json({ error: err.message });
}
},
};
Here's how I generate token on the registration and login requests:
const payload = { id: new_user._id };
const JWToken = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
The code provided is from the server, hence I don't know how its being saved on the client side, usually it is done using localhost, cookie or session. But you have mentioned that you are not using cookie or session, hence there is a chance that you are using local storage to store the jwt token. You can check your local storage on chrome by going to developer options -> Application -> Local Storage. You may find your token by how you named it, you can access it and delete by localStorage.removeItem("name of your token");

HttpOnly Cookies not set to browser (client side with laravel)

NEED YOUR EXPERIENCE AND KNOWLEDGE.
im building a web application where client-side using Laravel 8 (fckid.test using valet or http://localhost:8000) and Node.js (http://localhost:3000) for server-side API.
Serverside using JWT for authorization and sending the token through Httponly Cookie.
My problem is when testing it with Postman and ThunderClient, it works perfectly as shown by picture below
here is on postman
here is how my backend looks like
1.Router
const usersController = require('../controllers/users.controller')
const express = require('express')
const router = express.Router()
router.post('/login', usersController.login)
The Controller
exports.login = (req, res, next) => {
const data = {
email: req.body.email,
password: req.body.password
}
console.log('Request made by :', req.headers)
usersService.login(data, (error, results) => {
if (error) {
console.error(error)
res.status(error.statusCode).send({
msg_head: "error",
msg_body: "Internal Server Error",
data: error
})
} else if (results.status) { //if status = true
console.log(results)
res
.status(results.statusCode)
.cookie("accessToken", results.accessToken, {
httpOnly: true,
path: '/',
sameSite: 'none',
domain: 'fckid.test'
})
.send({
msg_head: "success",
msg_body: results
})
} else {
console.error(results)
res.status(results.statusCode).send({
msg_head: "error",
msg_body: results
})
}
})
}
this is how my laravel client-side looks like
route (im using web.php route)
Route::post('/auth-login', [LoginController::class, 'login'])->name('auth-login');
LoginController
class LoginController extends Controller
{
public function login(Request $request)
{
$body = $request->post();
$response = Http::post('http://localhost:3000/users/login', $body);
if ($response->status() == 200) {
return redirect()->route('view_student');
// return $response;
} else {
return redirect()->route('login.page');
}
}
}
what i already tried are
give exception to this route to VerifyCsrfToken.php
making request from Microsoft Edge, Firefox and Chrome
but those attempts still give me the same result where httponly cookie is not set to the browser.
I try to $request->getHeaders() and it igives me correct response where i taught that the server already send correct information as expected.
on browser it looks like laravel overide the results from server
it's been 2 days looking for some information that can help me to solve this problem on youtube, google, but no one really talk about this. hope you guys could help me out of this stuck.
Thanks
Cannot send "httponly" value as false by PHP after new browser updates.
If you need to use your cookie on the JS side, set your cookie on the client side with document.cookie.
document.cookie = "test1=Hello; SameSite=None; Secure";

Why is 'currentUser' and 'onAuthStateChanged' in firebase always null?

What I want to achieve
A user, who logged in or signed up should not re-login after one hour. The restriction of one hour comes from firebase authentication, if not prevented (what I try to accomplish).
Problem
After a user is logged in via firebase authentication (signInWithEmailAndPassword) I always get null for currentUser and onAuthStateChanged.
What I tried
I'm using React (v17.0.2) using 'Create React App'. On server side I'm using NodeJS (v12). The communication between both is accomplished using axios (v0.21.1)
First I tried to send the token stored in localStorage, which came from firebase (server side), back to the server. But the server tells me, that the token is no longer valid. Server side code as follows:
module.exports = (request, response, next) => {
let idToken;
if (request.headers.authorization && request.headers.authorization.startsWith('Bearer ')) {
idToken = request.headers.authorization.split('Bearer ')[1];
console.log("idToken:", idToken);
} else {
console.error('No token found');
return response.status(403).json({ error: 'Unauthorized' });
}
admin
.auth()
.verifyIdToken(idToken)
.then((decodedToken) => {
console.log('decodedToken', decodedToken);
request.user = decodedToken;
return db.collection('users').where('userId', '==', request.user.uid).limit(1).get();
})
.catch((err) => {
console.error('Error while verifying token', err);
return response.status(403).json(err);
});
};
After that I tried the following code on client side.
handleSubmit = () => {
const userData = {
email: this.state.email,
password: this.state.password
};
axios
.post(firestoreUrl() + '/login', userData)
.then((resp) => {
console.log("token:", resp.data); //here I get a valid token
localStorage.setItem('AuthToken', `Bearer ${resp.data.token}`);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //but this is null
})
.then((data) => {
console.log(data);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //still null
})
.catch((error) => {
console.log("Error:", error);
});
};
What irritates me is that I get a token from firebase (server side), the token is then stored in localStorage (client side) but firebase then tells me, that the currentUser is null. But presumably they are not mutually dependent =/.
I'm able to access all secured sites in my app. I can log out and in again. But whatever I do the currentUser is null.
I also tried to run the code above in componentDidMount()-method. But no success.
I tried an approach from this link (hopefully in a way it should be), but it didn't work. Still getting null for both currentUser and onAuthStateChanged if I implement following code.
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log("state = definitely signed in")
}
else {
console.log("state = definitely signed out")
}
})
I always get logged to the console, that the user is 'definitely signed out'.
During research I noticed that the point at which I should try to get the currentUser-Status is kind of tricky. So I guess that one solution is to implement the currentUser-code at another/the right place. And here I'm struggling =/.
As I found out at a similar question here on SO, I did a bad mistake. Apparently, it's not a good idea to perform the signIn- or createUser-functionality on server side. This should be done on client side. In the question mentioned above are some good reasons for doing that on server side but in my case it's quite ok to run it on client side.
Thanks to Frank van Puffelen for leading the way (see one of the comments in the question mentioned above).

How to send Bearer token to client and then call token from client

I have done a tutorial trying to get my head around JWT tokens. I seem to have got my head around the token creation as well as using the token to allow or disallow access to a route.
This all works great using postman, but in postman I enter the token under authorization. My question is:
1. how do I send the token to the client so it is saved on that client side.
2. How does the client return the token when they try to access a route?
I need to understand how this happens when NOT using postman. I am sure its pretty simple.
Do I just send
`res.header('Authorization', 'Bearer', + token);`
`res.header('Authorization', 'Bearer' + token);`
But can I send this with other stuff like a message / data etc?
Then when the user tries to access a protected route later, How do I access this header. IOW how is it stored client-side?
This is what I have thus far:
`//login route`
`app.post('/login', async function(req, res, next) {
const { name, password } = req.body;
if (name && password) {
let user = await getUser({ name: name });
if (!user) {
res.status(401).json({ message: 'No such user found' });
}
if (user.password === password) {
// from now on we'll identify the user by the id and the id is the
// only personalized value that goes into our token
let payload = { id: user.id };
let token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ msg: 'ok', token: token });
} else {
res.status(401).json({ msg: 'Password is incorrect' });
}
}
});`
`// protected route
app.get('/protected', passport.authenticate('jwt', { session: false }), function(req, res) {
console.log('REQUEST HEADERS ON PROTECTED::',req.headers.authorization)
res.json('Success! You can now see this without a token.');
});`
The console.log under protected route gives me:
"REQUEST HEADERS ON PROTECTED:: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTU2NjI3NTczfQ.gAU2VzpUpXHpcgM6_n8gf7D-xLCS59tK6K2RIlIk-L4" but I gather this is because I used the authorization in postman.
I recently worked with jwt auth using react as my front end and hapi.js as backend. To save the token on the client side, you can use localstorage like this:
You have to save this on the user login component.
localStorage.setItem('token', res.data.token);
And then, to access this token on the protected router, use this :
let token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
I hope this may help you to solve your problem on the client side.

Resources