I'm having a problem using Axios with my backend. It's probably a very simple fix as I'm new to this.
Postman: The correct response is received for both valid and invalid credentials.
Axios: The correct response is received for valid crendentials, but the axios method's catch block is run when invalid credentials are entered.
authController.js:
exports.login = (req, res, next) => {
const email = req.body.email;
const pass = req.body.password;
let loadedUser;
User.findOne({ where: { email: email } })
.then(user => {
if(!user) {
const error = new Error('Incorrect username or password');
error.statusCode = 401;
throw error;
} else {
loadedUser = user;
return bcrypt.compare(pass, user.password);
}
})
.then(isEqual => {
if(!isEqual) {
const error = new Error('Incorrect username or password');
error.statusCode = 401;
throw error;
} else {
const token = jwt.sign(
{
email: loadedUser.email,
userId: loadedUser.id
},
process.env.JWT_SECRET,
{ expiresIn: '1hr' }
);
res.status(200).json({ token: token, userId: loadedUser.id });
}
})
.catch(err => {
if (!err.statusCode)
err.statusCode = 500;
next(err);
});
};
The error handler in app.js. It seems to log the error correctly when incorrect credentials are entered, even with axios:
app.use((error, req, res, next) => {
const status = error.statusCode || 500;
const message = error.message;
const data = error.data || 'No Data';
console.log(status, message, data);
res.status(status).json({message: message, data: data});
});
But then the axios catch block runs, so instead of receiving the json message, I get the following error
login(email, password) {
const headers = {
'Content-Type': 'application/json'
};
const data = JSON.stringify({
email: email,
password: password
});
axios.post('http://127.0.0.1:8080/auth/login', data, { headers })
.then(res => console.log(res))
.catch(err => console.log(err));
}
The error in the console for invalid credentials:
Clicking the link highlighted opens a new page stating: "Cannot GET /auth/login", but I'm obviously making a post request, & I've added post to the form (just in case)
Any ideas what I could be missing?
Thanks
Actually your code works fine but Axios will reject the promise of the call if you have the status 401. If you have a status between 200 to 300 it will resolve the promise.
There two ways to deal with this.
Check status in the catch block.
axios.post('http://127.0.0.1:8080/auth/login', data, {
headers
})
.then(res => console.log(res))
.catch(err => {
if (err.response.status === 401) {
//Auth failed
//Call reentry function
return;
}
return console.log(err)
});
or change the validateStatus option;
axios.post('http://127.0.0.1:8080/auth/login', data, {
headers,
validateStatus: function (status) {
return status >= 200 && status < 300 || (status === 401);
},
})
.then(res => console.log(res))
.catch(err => return console.log(err));
Related
Hi I am testing to see if it authorizes me to get orders. Currently BeforeAll is reading the token but I still receive 401 when I run my test. What is going wrong in my code? I am using Jest and Supertest.
This is my test file.
import app from '../server'
import request from 'supertest'
jest.useFakeTimers()
let token
beforeAll((done) => {
request(app)
.post('/api/users/login')
.send({
email: 'test15#email.com',
password: '123456'
})
.end((err, response) => {
token = response.body.token; // saving token
done();
});
});
describe('app', () => {
describe('Statuses', () => {
it('Index should return 200 Status', () => {
return request(app)
.get('/api/orders')
.set('Authorization', `Bearer ${token}`)
.then((response) => {
expect(response.statusCode).toBe(200);
})
});
});
});
I save the token beforehand then I run my test. I've looked everywhere but can't seem to find a solution.
auth.js
const protect = asyncHandler(async(req,res,next) => {
let token
if(
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
){
console.log('token found')
}{
try {
token = req.headers.authorization.split(' ')[1]
const decoded = jwt.verify(token, process.env.JWT_SECRET)
req.user= await User.findById(decoded.id).select('-password')
next()
} catch (error) {
console.error(error)
res.status(401)
throw new Error('Not authorized, token failed')
}
}
if (!token){
res.status(401)
throw new Error('Not Authorized, no token')
}
})
As long as you only need to test if response is ok, instead of using Jest expect, did you try using the expect from superset? something like:
it('should return 200 Status', () => {
request(app)
.get('/api/orders')
.set('Authorization', `Bearer ${token}`)
.expect(200, done);
});
I'm trying to scrape some data from truepush website, but first it needs to be authenticated. So here is what I'm doing:
const loginUrl = 'https://app.truepush.com/api/v1/login'
let loginResult = await axios.get(loginUrl)
.then(({ headers }, err) => {
if (err) console.error(err);
return headers['set-cookie'][0];
})
.then((cookie, err) => {
if (err) console.error(err);
const splitByXsrfCookieName = cookie.split("XSRF-TOKEN=")[1]
return splitByXsrfCookieName.split(';')[0];
}).then(xsrfToken => {
return axios.post(loginUrl, {
headers: {
"Content-Type": "application/json",
"X-XSRF-TOKEN": xsrfToken
}
})
}).then(res => console.log(res))
It throws xrsfToken on second then response and when I try to login in third response with that xsrf token, it shows me this error:
{
"status_code": "XSRF-ERROR",
"status": "ERROR",
"message": "Cross domain requests are not accepting to this endpoint. If you cleared the cookies, please refresh your browser."
}
I'm not sure what wrong I'm doing :(
The main issue is that the call also requires the original cookie to be sent. You need to keep the original cookie your get from set-cookie header and pass it in cookie header in the second call like cookie: originalCookie. Also in your code, there is no body sent in the POST call.
The following code reproduces the login :
const axios = require("axios");
const originalUrl = 'https://app.truepush.com';
const loginUrl = 'https://app.truepush.com/api/v1/login';
const email = "your-email#xxxxxx";
const password = "your-password";
(async () => {
await axios.get(originalUrl)
.then(({ headers }, err) => {
if (err) console.error(err);
const cookie = headers['set-cookie'][0];
return {
cookie: cookie,
xsrfToken: cookie.split("XSRF-TOKEN=")[1].split(";")[0]
};
})
.then((data, err) => {
if (err) console.error(err);
return axios.post(loginUrl, {
"email": email,
"password": password,
"keepMeLoggedIn": "yes"
}, {
headers: {
"X-XSRF-TOKEN": data.xsrfToken,
"cookie": data.cookie
}
})
})
.then(res => console.log(res.data))
})();
Output:
{
status_code: 'SUCCESS',
status: 'SUCCESS',
message: 'Login Successful',
data: {
id: 'xxxxxxxxxxxxxxxxxxx',
name: 'xxxxx',
email: 'xxxxxxx#xxxxxx'
}
}
Note that both cookie and xsrfToken are consumed by the second promise
Hello developers the question is simple,
I have generated a jwt token in my Login function using the jwt.sign(), and I have Model/Controller/Router Architecture,
so the question is : How can I pass the generated token from the Login controller function to the router.
I've tried many times to assign the token to a const variable to send it throw an object and send it to the router files, but when I go out from the jwt.sign() function it shows me that is undefined.
PS : I'am just using NodeJS and fastify in the backend and send http request with Postman am not using any framework in the front-end
There is some code than can help you to understand my situation :
UserRouter.js: (Login route) :
{
method: "POST",
url: "/api/login",
handler: (req, res) => {
UserController.login(req.body.email, req.body.password)
.then(result => {
//res.header("Access-Control-Allow-Origin", URL);
if (result.statusCode == 200) {
res.send({
status: 200,
error: null,
response: result.Message
//token: result.token
});
} else if (result.statusCode == 401) {
res.send(
JSON.stringify({
status: 401,
error: null,
response: result.Message
})
);
}
})
.catch(err => {
//res.header("Access-Control-Allow-Origin", URL);
res.send(JSON.stringify({ status: 300, error: err, response: null }));
});
}
}
User Controller :
exports.login = async (user_email, password) => {
try {
console.log("Login into API");
const email = user_email.toLowerCase();
const user = await User.findOne({ email });
if (user) {
console.log(" Hashed Passwd ", user.password);
console.log("User Passwd", password);
let result = await bcrypt.compareSync(password, user.password);
if (result) {
// Tryed also with const = await jwt.sign()
jwt.sign({ user }, "secretkey", (err, token) => {
if (err) throw err;
console.log("The Token is", token);
});
return {
Message: "Login success",
statusCode: 200
//token: token
};
} else {
return { Message: "Incorrect password", statusCode: 401 };
}
} else {
return { Message: "ERROR" };
}
} catch (err) {
throw boom.boomify(err);
}
};
if you look at the package readme, you'll find jwt.sign returns nothing when a callback is provided.
So what you should do is:
const token = jwt.sign({ user }, "secretkey");
That would make the library work synchronously and return the token.
I am running a react app with nodejs acting as an api to connect to my database.
For my log in I am sending data to the server, and it is returning a pass or fail.
However I am not sure how to extract this json object.
I have looked at the request and response, and as I have manipulated the json object the response content-length has been changing so I believe it must be there somewhere.
SERVER CODE:
app.post('/api/checkLogin', async (req,res) => {
console.log(req.body);
const {username, password} = req.body;
try{
let state = await DB.checkPassword(username, password);
console.log(state);
if(!state){
res.status(401).json({
error: 'Incorrect username or password',
yay: 'idk work?'
});
}
else if(state){
res.status(200).json({
message: 'we in boys'
});
} else {
res.status(6969).json({
err: 'idk mane'
});
}
} catch(e) {
console.log(e);
}
})
CLIENT CODE:
onSubmit = (event) => {
event.preventDefault();
fetch('/api/checkLogin', {
method:'POST',
body: JSON.stringify({username: this.state.username, password: md5(this.state.password)}),
headers: {
'Content-Type':'application/json'
}
}).then(res => {
if(res.status ===200) {
this.props.loggedIn();
} else if(res.status ===401){
console.log(res.status);
alert('wrong username or password');
}else{
const error = new Error(res.error);
throw error;
}
}).catch(err => {
console.log(err);
alert(err);
});
}
What I was sort of expecting as a way to extract the data would be.
On the server:
res.status(200).json({ message : 'mssg'});
On the client:
console.log(res.status.message) // 'mssg'
Thanks Jin and this post I found for the help Fetch API get raw value from Response
I have found that both
res.status(xxx).json({ msg: 'mssg'}) and res.status(xxx).send({msg: 'mssg'}) work.
The json, or sent message can then be interpreted on the client side with a nested promise. This is done with...
fetch('xxx',headers n stuff).then(res => {
res.json().then((data) => {console.log(data.message)});
//'mssg'
res.text().then((data) => { let data1 = JSON.parse(data); console.log(data1.message);});
//'mssg'
});
According to my experience, using res.status(200).send({message: 'mssg'}) is better.
And you can get data after calling api by using res.data.
Then you can get result as below:
{
message: 'mssg'
}
Here is something that may help.
onSubmit = (event) => {
event.preventDefault();
const userData = {
username: this.state.username, // I like to store in object before passing in
password: md5(this.state.password)
}
fetch('/api/checkLogin', {
method:'POST',
body: JSON.stringify(userData), //stringify object
headers: {
'Content-Type':'application/json'
}
}).then(res => res.json()) // convert response
.then(responseData => {
let status = responseData.whatObjectWasPassedFromBackEnd;
status === 200 ? do something on pass: do something on fail
})
.catch(err => {
console.log(err);
alert(err);
});
}
I can't seem to get the correct response headers when my code enters bcrypt.compare. I thought it was a cors issue at first but I still get the correct response if I entered the wrong and "user does not exist" is displayed.
Here's my api server side code in express
router.post("/api/signIn", (req, res) => {
const { user, password } = req.body;
const queryString = "SELECT * FROM users WHERE user_id = ?";
db.query(queryString, [user])
.then(result => {
if (result.length > 0) {
const hash = result[0].password;
//here bcrypt.compare works server side or with cURL but won't set the response headers in the browser
bcrypt
.compare(password, hash)
.then(same => {
if (same === true) {
console.log("correct password");
res.status(200).json({
code: 200,
message: "correct password"
});
} else {
res.status(401).json({
code: 401,
message: "incorrect password"
});
}
})
.catch(err => console.log(err));
} else {
//this one works though and i can get the response in the browser so it can't be a cors issue
console.log("user does not exist");
res.status(200).json({
code: 401,
message: "User does not exist"
});
}
})
.catch(err => {
console.log("error" + err.message);
});
});
and this is the test function i use in react
const signIn = () => {
fetch("http://localhost:5000/api/signIn", {
method: "POST",
body: JSON.stringify({
user: userName,
password: password
}),
headers: {
"Content-Type": "application/json"
},
})
.then(res => res.json())
.then(response => alert(response.code + response.message))
.catch(err => alert(err));
};
so if i entered the wrong username that is not in the database, the alert function would show (code401User does not exist) but if i entered the correct user bcrypt.compare() doesn't seem to set the response for both correct and incorrect passwords and i would get (TypeError: failed to fetch). testing the api in cURL works though.
Got it, I forgot to put event.preventDefault() on the fetch function.