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
Related
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 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
If API is accessed as GET request from the browser as authenticated, you get the data.
But when you want to access the data by fetching in client-side by the function getserverside gives me the error that is not authenticated which it fact it is.
API
export default async (req, res) => {
const { method } = req;
const cookie = {
headers: {
cookie: req.headers["cookie"],
},
};
const session = await getSession({ req: cookie });
if (!session) {
return res.status(401).json({
success: false,
message: "NOT Authorized",
});
}
switch (method) {
case "GET":
try {
const jobs = await prisma.jobs.findMany({
where: {
userId: session.id,
},
});
return res.status(200).json({ success: true, jobs });
} catch (error) {
return res.status(404).json({
success: false,
message: error.message,
});
}
Component
export async function getServerSideProps({ req, res }) {
console.log(req.cookies); // Logs all cookies from the request
const cookie = {
headers: {
cookie: req.headers["cookie"],
},
};
const session = await getSession({ req: cookie });
console.log(session); // session is received perfectly.
const res = await axios.get(`${process.env.API_URL}/company/jobs`); // ERROR NOT AUTHENTICATED
}
It is a bit weird because when I try to access the data the cookie is received but when I make the request it says is not authenticated
According to documentation, you should pass to getSession function whole original req in API. and whole context prop in getServerSideProps
https://next-auth.js.org/getting-started/client#getsession
I'm not sure how to check if the values match with the MongoDB data. I am using PUT and trying to use findOneAndUpdate to check if the values match.
<script>
const logindetails = new Vue({
el: '#logindetails',
data: {
email: "",
password: "",
on: Boolean
},
methods: {
login: function (e) {
e.preventDefault();
const log = {
email: this.email,
password: this.password,
}
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(log)
};
fetch('http://localhost:3000/users/${this.email}/${this.password}',
options).then(response => {
[...]
</script>
This is the server code (it successfully connected to MongoDB) :
app.put('/students/:email/:password', (req, res, next) => {
console.log("login");
res.setHeader("Content-Type", "application/json");
db.collection('users').findOne({email: (req.params.email), password: (req.params.password)},
{$set: {on: true}})
.then(results => res.send(results))
.catch(err => res.send(err))
});
I personally don't think it is a good idea to put your username and password as query string, because it hurts the restful api convention. It wouldn't make sense to use a put request if there is no body being pass. Also, a post request would make more sense in a login situation .Anyway I digress, here are the usual steps to doing authentication.
1. (Client-Side) Send the email and password in the body of the fetch request
//something like this
const body = { email, password };
const response = await fetch(
"http://localhost:5000/authentication/login",
{
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(body)
}
);
2.(Server-Side - make sure you to use app.use(express.json()) to access req.body)
//defining middleware to access req.body
app.use(express.json());
app.post("/authentication/login", async(req,res) =>{
//1. destructure email and password
const {email, password} = req.body
//2. check if user doesn't exist
const user = await db.user.find({user_email: email})
if(!user){
return res.status(401).send("User does not exist");
}
//3. Check if password is the same as the password in the database
if(password !== user.password){
return res.status(401).send("Wrong Credential")
}
//4. This is up to you when the user is authenticated
res.json(`Welcome back ${email}`);
})
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.