Jest testing API Expect 200 but received 401 - javascript

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);
});

Related

SyntaxError: Unexpected end of JSON input at callAboutPage (About.js:25:1)

I am trying to fetch the currently logged-in user's data using the jwt tokens. The API routing for the token verification is working fine but the data I think the (rootUser) from auth.js sent as response might have a problem although it is giving correct values on console.log(rootUser) but when i tried to access the about page on the browser it gave the error
SyntaxError: Unexpected end of JSON input
at callAboutPage (About.js:25:1) that is at line 25 for the About.js file. How to solve this issue?
This is code to About.js where i have defined a callabout page function to behave necessarily on accessing the about page on browser:
const About = ()=>{
const navigate = useNavigate();
const [userData, setUserData] = useState({});
const callAboutPage = async() =>{
try {
const res = await fetch("/about",{
method: "GET",
headers: {
Accept: "application/json",
"Content-Type" : "application/json"
},
credentials: "include"
});
const data = await res.json();
console.log(data);
// setUserData(data);
if(res.status === 401 || res.status===403){
const error = new Error(res.error);
throw error;
}
} catch (err) {
console.log(err);
navigate("/login");
}
}
useEffect(()=>{
callAboutPage();
}, []);
}
This is authenticate.js file where tokens are verified and corresponding user data is finded
const Authenticate = async(req, res, next) => {
const accessToken = req.cookies.jwt;
if(!accessToken) {
return res.status(401).json({error: "Unauthorized: No token provided"});
}
try {
const user = jwt.verify(accessToken, process.env.TOKEN_KEY);
const rootUser = await User.findOne({_id: user.user_id, "tokens.token": accessToken});
if(user) {
req.user = rootUser;
return next();
}
} catch (error) {
return res.status(403).json({error: "Forbidden token error"})
}
}
This is auth.js file to call /about at backend
router.get("/about", Authenticate, function (req, res) {
console.log("about running");
res.send(req.rootUser);
});

How to refresh accesstoken in vuejs without doing a full page refresh using axios interceptors

I am trying to request a new access token using a refresh token if the current access token is expired. I have the following code setup for this
axios.interceptors.response.use(undefined, function(error) {
if (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
axios.post("auth/refresh", {
refreshToken: store.state.refreshToken
}).then(res => {
store.dispatch('setToken', res.data.token)
store.dispatch('setRefToken', res.data.refreshToken)
error.config.headers[
"Authorization"
] = `Bearer ${res.data.token}`;
})
} else {
return Promise.reject(error);
}
}
})
This seems to work and get the new access token from the server.
The problem now is this, I need to refresh the page for the new Auth Headers to be set and that is not ideal since a logged-in user may be performing an action and may not know to refresh the page after the token expires.
How can I achieve this and ensure the user does not experience any glitch?
axios.interceptors.response.use(
(res) => {
return res;
},
async (err) => {
const originalConfig = err.config;
if (originalConfig.url !== "/login" && err.response) {
if (err.response.status === 401 && !originalConfig._retry) {
originalConfig._retry = true;
try {
const rs = await axios.post("auth/refresh", {
refreshToken: store.state.refreshToken
});
const {
token,
refreshToken
} = rs.data;
store.dispatch('setToken', token)
store.dispatch('setRefToken', refreshToken)
err.config.headers[
"Authorization"
] = `Bearer ${token}`;
return new Promise((resolve, reject) => {
axios.request(originalConfig).then(response => {
resolve(response);
}).catch((err) => {
reject(err);
})
});
} catch (_error) {
return Promise.reject(_error);
}
}
}
return Promise.reject(err);
}
);
Update: I did a lot of digging and read through a lot of articles to see what was wrong with my initial code and approach.
And I found a fix that suits my use case perfectly. It's a bit messy but works.

Unable to get the required redirect_uri in react-facebook-login

I'm trying to implement the Facebook OAuth in my express/NodeJS app using authorization code flow. I'm using react-facebook-login node module to fetch the authorization code. In my react app, I could get the authorization code successfully. But in server side, I can't request the access token from the Facebook API as I'm getting an error message "redirect_uri is not identical to the one you used in the OAuth dialog request"
Code in my react app,
facebookLogin = async (signedRequest) => {
return fetch('/api/auth/facebook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ signedRequest }),
}).then((res) => {
if (res.ok) {
return res.json();
} else {
return Promise.reject(res);
}
});
};
responseFacebook = async (response) => {
try {
if (response['signedRequest']) {
const userProfile = await this.facebookLogin(response['signedRequest']);
console.log(userProfile);
} else {
throw new Error(response.error);
}
} catch (err) {
console.log(err);
}
};
render() {
<FacebookLogin
appId={process.env.FACEBOOK_CLIENT_ID}
fields="name,email"
responseType="code"
redirectUri="http://localhost:3000/"
callback={this.responseFacebook}
/>
In my app.js
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const signedRequest = req.body.signedRequest;
const profile = await facebookOAuth.getProfile(signedRequest);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});
facebookOAuth.js look like this
const fetch = require('node-fetch');
const getData = async (userId, accessToken) => {
const userData = await fetch(`https://graph.facebook.com/${userId}?fields=name,email&access_token=${accessToken}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((userData) => {
return userData;
});
return userData;
};
exports.getProfile = async (signedRequest) => {
const decodedSignedRequest = JSON.parse(Buffer.from((signedRequest.split(".")[1]), 'base64').toString());
const profile = await fetch(`https://graph.facebook.com/oauth/access_token?client_id=${process.env.FACEBOOK_CLIENT_ID}&redirect_uri=${encodeURIComponent('http://localhost:3000/')}&client_secret=${process.env.FACEBOOK_CLIENT_SECRET}&code=${decodedSignedRequest.code}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((token) => {
console.log(token);
const userData = getData(decodedSignedRequest.user_id, token.access_token);
return userData;
}).catch((err) => {
console.log(err);
return err;
});
return profile;
}
What I'm getting is this error
"error": {
message: 'Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request',
type: 'OAuthException',
code: 100,
error_subcode: 36008,
fbtrace_id: 'A-YAgSqKbzPR94XL8QjIyHn'
}
I think the problem lies in my redirect_uri. Apparently, the redirect uri I obtained from the Facebook auth dialog is different from the one that I'm passing to the facebook API in my server side (http://localhost:3000/).
I believe there's something to do with the origin parameter of the redirect_uri. Initial auth dialog request uri indicates that it's origin parameter value is something like "origin=localhost:3000/f370b6cb4b5a9c". I don't know why react-facebook-login add some sort of trailing value at the end of origin param.
https://web.facebook.com/v2.3/dialog/oauth?app_id=249141440286033&auth_type=&cbt=1620173773354&channel_url=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f39300d6265e5c4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&client_id=249141440286033&display=popup&domain=localhost&e2e={}&fallback_redirect_uri=http://localhost:3000/&locale=en_US&logger_id=f1b3fba38c5e31c&origin=1&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f17641be4cce4d4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&frame=f3960892790a6d4&response_type=token,signed_request,graph_domain&return_scopes=false&scope=public_profile,email&sdk=joey&version=v2.3
I tried finding everywhere about this but no luck. Anyone has clue about this, much appreciated.
Are you using middleware to parse the body? if you aren't code could be undefined here.
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const code = req.body.code;
const profile = await facebookOAuth.getProfile(code);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});

How to pass jwt token from controller to router using NodeJS

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.

Error using Axios, but correct response in Postman

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));

Categories