I have this code in node js API :
const jwt = require("jsonwebtoken");
generateToken = (user, res) => {
const token = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "1800s",
});
res
.cookie("token", token, {
httpOnly: true,
})
.status(200)
.json({ message: "Logged in successfully 😊 👌" });
};
module.exports = generateToken;
I have this code in Next js project :
const onSubmitLogin = (data) => {
axios
.post(
`http://localhost:8000/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
}
)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
If I use Postman, i get the cookie with the token.
But, when I use the browser I dont get the cookie stored in cookies.
I tried to add withCredentials: true, in axios request but nothing changes.
However, I get the message "Logged in successfully 😊 👌" in the browser's console
Related
I am using koa.js as a node.js server and setting a jwt token as a cookie. It gets set correctly. When a user authenicates, the cookie is set. In response headers, I can see the set-cookie attribute
Set-Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzYjZlMWUwYTFhNGMxOTQzNDgzNWVkZCIsInVzZXJuYW1lIjoibml4ZW4iLCJpYXQiOjE2NzMzODM3NDZ9.U_RNtDXVSw9p7B3GJ02abx95hCYHtKmY5oN7Hutxv_k; path=/; expires=Tue, 10 Jan 2023 20:49:06 GMT; samesite=none; httponly
Below is how I am setting the cookie
export const checkUserCredentials = async (ctx, username, password) => {
try {
const user = await ctx.db.collection('user').findOne({ username });
if (!user) {
return { status: 'error', message: 'Invalid username or password' };
}
if (await bcrypt.compare(password, user.password)) {
const token = jwt.sign(
{ id: user._id, username: user.username },
process.env.JWT_SECRET
);
ctx.cookies.set('jwt', token, {
httpOnly: true,
expires: new Date(Date.now() + 365),
secure: process.env.NODE_ENV === 'production' ? true : false,
sameSite: 'none',
});
return { status: 'ok', data: token };
}
return { status: 'error', message: 'Invalid username or password' };
} catch (err) {
logger.error(err.message);
return { status: 'error', message: err.message };
}
};
When I use ctx.cookies.get('jwt') // returns undefined in a different request after the user has been logged in, I get undefined. What am I doing wrong?
The request I use to get a cookie is a test endpoint which goes like below
UserRouter.get('/api/test', koaBody(), testUser);
and the testUser function
export const testUser = async (ctx) => {
console.log(ctx.cookies.get('jwt'), ' cookie');
ctx.body = { status: 'ok' };
};
Also, I am using fetch to send request. My function that sends the api request is below
const testing = async (e) => {
e.preventDefault();
const result = await fetch('/api/test', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
}).then((res) => res.json());
};
I can provide more information if required.
i´m creating a Authentication page with React and Express. I'm using JWT too.
I´ve made this route in the back:
server.js
...
app.use(
cookieSession({
name: "prode_session",
secret: "MIOURI_PRODE_SECRET", //add to .env variable
httpOnly: false,
})
);
app.use(cors());
...
auth.routes.js
app.post("/signin", controller.signin);
user.routes.js
app.get(
"/user",
[authJwt.verifyToken],
(req, res) => res.send(true)
)
auth.controller.js
exports.signin = async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email },
});
try {
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
const passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({
message: "Invalid Password!",
});
}
const token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 84000, //24hours
});
req.session.token = token;
console.log(req.session);
return res.status(200).send({
isLogged: true,
id: user.id,
email: user.email,
suscripcion: user.suscripcion,
preference_id: user.preference_id,
token,
});
} catch (error) {
console.log(error);
}
};
authJWT.js
verifyToken = async (req, res, next) => {
let token = req.session.token;
console.log(`THIS IS THE TOKEN: ${token}`);
if (!token) {
return res.status(403).send({
message: "No token provided",
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
console.log(err);
return res.status(401).send({
message: "Unauthorized!",
});
}
req.id = decoded.id;
next();
});
};
const authJwt = { verifyToken };
module.exports = authJwt;
When I test this with POSTMAN, it works Ok, I mean, if first I try to make the GET request, the response is "No token provided", but if I signin first, generate the token and then make the GET request, I get true.
The problem is when I try to implement this in the front.
I have this Login component in React in which I make a POST request with the credentials:
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:3000/signin", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await response.json();
console.log(data);
if (data.isLogged && data.suscripcion === true && data.token) {
await tokenAvailable()
//navigate(`/masthead/${email}&${data.isLogged}&${data.id}`);
} else if (data.isLogged && data.suscripcion === false) {
navigate("/suscripcion", {
state: { preference_id: data.preference_id },
});
} else {
window.alert("Invalid Login");
}
} catch (error) {
console.log(error);
}
};
async function tokenAvailable() {
const user = await fetch("http://localhost:3000/user", {
method: "GET",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
const response = await user.json();
setUser(await response);
console.log(await response);
return response;
}
When I make the POST, the GET request is executed (tokenAvailable function) after receiving the response, but I receive "No token Provided" while I expect to receive "true" as in Postman.
From what I debug, the authJWT.js file, is not receiving nothing from the req.session.token.
When I compare the headers from postman and the browser, in postan the SET-cookie key appears, but in the browser not.
postman:
browser:
I need some help here. I´ve been strugling with this for almost 3 days.
I found a solution for this. Apparently, the HttpOnly Cookie approach works if the React app and the back-end server hosted in same domain. So we need to use http-proxy-middleware for local development.
I´ve tried to install the http-proxy-middleware but a lot of errors came, so I decided to store de JWT in the localstorage.
I am trying to configure my JWT through http-only cookies
const signIn = async (req, res) => {
const { email } = req.body;
try {
const user = await User.findOne({ email });
const access_token = jwt.sign({ id: user._id }, jwtSecret, {
expiresIn: token_expiry_time,
});
const refresh_token = jwt.sign({ id: user._id }, jwtRefreshSecret, {
expiresIn: refresh_token_expiry_time,
});
res.cookie('access_token', access_token, { httpOnly: true });
res.cookie('refresh_token', refresh_token, { httpOnly: true });
return res.status(200).json({
status: true,
data: {
user: {
id: user._id,
},
},
});
} catch (err) {
Server.serverError(res, err);
}
};
but at the client-side it refuses to set the cookie in the browser, it returns the error "cannot set cookie because same-site is not 'None' ", After setting sameSite:'None' on the server side, it then gave the error "same-site set to 'None' needs to have a 'Secure' field", I then set secure:true on the backend but it doesn't work because I am not using https for development.
client-side code
const onSubmit = (cred) => {
setLoading(true);
restAPI
.post('auth/signin', cred)
.then(({ data }) => {
setLoading(false);
setError(false);
console.log(data.data);
})
.catch((err) => {
setLoading(false);
setError({
status: true,
message: err.response.data.message,
});
});
};
how can I solve this?
I am struggling to make a login system using JSON web tokens.
I have made the login (client side) that calls to my server.js file.
This is the login through the client side Below is my handle submit function that calls the server.js login route.How would I use a token here?
handleSubmit(e) {
e.preventDefault();
if (this.state.email.length < 8 || this.state.password.length < 8) {
alert(`please enter the form correctly `);
} else {
const data = { email: this.state.email, password: this.state.password };
fetch("/login", {
method: "POST", // or 'PUT'
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(data => {
console.log("Success:", data);
})
.catch(error => {
console.error("Error:", error);
});
}
}
catch(e) {
console.log(e);
}
This is the login route for my server.js. As you can see I have assigned a jwt but how would I send this back to my login form and utilise it for protected routes.
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
const result = await request.execute("dbo.LoginUser");
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
const token = jwt.sign({ user: Email }, "SECRET_KEY", {
expiresIn: 3600000
});
var decoded = jwt.verify(token, "SECRET_KEY");
console.log(decoded);
response.status(200).json({
ok: true,
user: Email,
token: token
});
console.log(token);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
Essentially all I want is for my submit handler to be called for login. Server returns a jwt token which can then be used to verify other routes.
There are two ways to route:
Use React-Redux and react-router.
Save the fetched JWT token into localStorage and use to validate route within your routes component.
I would recommend in using React-Redux / React-router for protected routing.
Here is a video link to Build Real Web App with React by
Rem Zolotykh
This will help you.
I'm trying to do an authorisation middleware using jsonwebtoken, cookie-parser to set cookies and jQuery in the client.
For that I'm using a middleware to check authorisations.
here is the middleware
const jwt = require('jsonwebtoken')
module.exports = (req,res,next)=> {
try{
const token = req.headers.authorization.split("=")[1];
const decodedToken = jwt.verify(token, process.env.JWT_KEY)
req.userData = decodedToken;
next();
} catch(error){
return res.status(401).json({
message: "Auth failed"
})
}
}
In my API I'm creating and sending the token like this:
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
expiresIn: "1h"
}
)
res.cookie('userToken', token)
res.render('index', {
movies: {}, token:token
});
In the client I'm sending back to the server like this:
var userToken = document.cookie;
// console.log(userToken)
$.ajax({
url: "/movies/add",
type: "GET",
beforeSend: function (xhr, settings) {
console.log(xhr)
xhr.setRequestHeader('Authorization', 'Bearer ' + userToken);
}
});
The movies/add route is receiving the request, because it's printing 'it entered', but it's not redirecting to movies/addMovies. What am I missing?
router.get('/add', checkAuth, function (req, res, next) {
console.log('it entered')
res.render('movies/addMovies', {
movies: {}
})
});
You're requesting the "page" via an AJAX call and returning the HTML (res.render) to the JavaScript.
If you're wanting to navigate to the other screen, you should either use a hyperlink or redirect using JavaScript (location.href).
I've changed to this:
$.ajax({
url: "/movies/add",
type: "GET",
data:{
'userToken':userToken
},
success: function(){
console.log('sim')
window.location.href = "/movies/add";
}
});
and in the server
const token = req.cookies.userToken
const decodedToken = jwt.verify(token, process.env.JWT_KEY)
and now it works