Change IP using Jest and Supertest - javascript

I'm following the documentation of nestjs to create my e2e tests.
Now I'd like to create requests and change the IP, because I have an interceptor accepts requests only from a range of ip.
Now I try to create it in my e2e class
const req = require('express/lib/request');
const CLIENT_IP = '1.2.3.4';
beforeEach(() => {
// Mock the `ip` property on the `req` object
let ip = jest.spyOn(req, 'ip', 'get').mockReturnValue(CLIENT_IP);
});
but the ip remains the same (my error: 'Not a valid IP ::ffff:127.0.0.1').
I tried to change the ip in my init setup like:
app.set('Remote-Addr', '1.1.1.1')
but the error is the same (and I don't like this approach.
How can I decide a specific ip for each requests?
I'm using express

I am not really sure why you need to mock the client ip address, you should be sending it as a http header e.g. x-client-id or x-forwarded-for.
Which means that if you are writing a NestJS e2e test using supertest, you should be able to manually set the ip-address in the header e.g.
import { IncomingHttpHeaders } from 'http';
import { Test, TestingModule } from '#nestjs/testing';
it('Create a new user -> (201) success', async () => {
const requestHeaders: IncomingHttpHeaders = {
'x-client-id': '1.1.1.1',
'content-type': 'application/json',
}
const requestBody: User = {
name: 'joe blogs',
age: '65'
}
const { statusCode, body } = await app.inject({
method: 'POST',
url: '/api/v1/users',
headers: requestHeaders,
payload: requestBody,
});
expect(statusCode).toEqual(HttpStatus.CREATED);
exoect(body).toBeDefined();
});

Related

how to get cookie in react passed from express js api (MERN stack)

I have an api in express js that stores token in cookie on the client-side (react). The cookie is generated only when the user logins into the site. For example, when I test the login api with the postman, the cookie is generated as expected like this:
But when I log in with react.js then no cookie is found in the browser. Looks like the cookie was not passed to the front end as the screenshot demonstrates below:
As we got an alert message this means express api is working perfectly without any error!!
Here is my index.js file on express js that includes cookie-parser middleware as well
require("dotenv").config();
const port = process.env.PORT || 5050;
const express = require("express");
const app = express();
const cors = require("cors");
const authRouter = require("./routes/auth");
var cookieParser = require('cookie-parser')
connect_db();
app.use(express.json());
app.use(cookieParser())
app.use(cors());
app.use("/" , authRouter);
app.listen(port , () => {
console.log("Server is running!!");
})
Code for setting up the cookie from express api only controller
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require('bcrypt')
const login = async (req, res) => {
const { email, password } = req.body;
try {
const checkDetails = await User.findOne({ email });
if (checkDetails) {
const { password: hashedPassword, token, username } = checkDetails;
bcrypt.compare(password, hashedPassword, function (err, matched) {
if (matched) {
res.cookie("token", token, { expires: new Date(Date.now() + (5 * 60000)) , httpOnly: true }).json({ "message": "You logged in sucessfully!" });
} else {
res.status(500).json({ "message": "Wrong password" });
}
});
} else {
res.status(500).json({ "message": "Wrong email" });
}
} catch (error) {
console.log(error.message);
}
}
Here is the react.js code that I am using to fetch data from api without using a proxy in package.json file
if (errors.length === 0) {
const isLogin = await fetch("http://localhost:5000/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json"
}
});
const res = await isLogin.json();
if(res) alert(res.message);
}
I want to get to know what is the reason behind this "getting cookie in postman but not in the browser". Do I need to use any react package?
The network tab screenshot might help you.
If I see in the network tab I get the same cookie, set among the other headers
To my understanding, fetch doesn't send requests with the cookies your browser has stored for that domain, and similarly, it doesn't store any cookies it receives in the response. This seems to be the expected behaviour of fetch.
To override this, try setting the credentials option when making the request, like so:
fetch(url, {
// ...
credentials: 'include'
})
or, alternatively:
fetch(url, {
// ...
credentials: 'same-origin'
})
You can read more about the differences between the two here.
I got my error resolved with two changings in my code
In front end just added credentials: 'include'
fetch(url, {
method : "POST"
body : body,
headers : headers,
credentials: 'include'
})
And in back end just replaced app.use(cors()); to
app.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag'] }))
That's it got resolved, Now I have cookies stored in my browser!!! Great. Thanks to this article:
https://www.anycodings.com/2022/01/react-app-express-server-set-cookie-not.html
during development i also faced same things, let me help you that how i solve it,
Firstly you use proxy in your react package.json, below private one:-
"private": true,
"proxy":"http://127.0.0.1:5000",
mention the same port on which your node server is running
Like:-
app.listen(5000,'127.0.0.1',()=>{
console.log('Server is Running');
});
above both must be on same , now react will run on port 3000 as usual but now we will create proxy to react So, react and node ports get connected on same with the help of proxy indirectly.
Now, when you will make GET or POST request from react then don't provide full URL, only provide the path on which you wants to get hit in backend and get response,
Example:-
React side on sending request, follow like this:-
const submitHandler=()=>{
axios.post('/api/loginuser',
{mobile:inputField.mobile,password:inputField.password})
.then((res)=>{
console.log(res);
})
.catch((err)=>{
console.log(err);
})
}
Node side where it will hit:-
app.post('/api/loginuser', async(req,res)=>{
//Your Code Stuff Here
res.send()
}
on both side same link should hit, it is very important
it will 100%.
don't forget to mention
on node main main where server is listening

Using ably.io JWT with Angular

I'm trying to use ably.io with Angular and Azure Functions using the JWT way of authenticating since it's secure, but I'm having issues with configuring the angular side of it. The use case is for a live auction site to update bids in realtime. There isn't a specific angular tutorial for this so I'm trying to piece it together. Also this code
realtime.connection.once('connected', function () {
console.log('Client connected to Ably using JWT')
alert("Client successfully connected Ably using JWT auth")
});
never throws the alert so I don't think it's working right. I used to have it working where I wasn't using ably JWT, but had the API key on the client-side in a component like this
let api = "<api key>";
let options: Ably.Types.ClientOptions = { key: api };
let client = new Ably.Realtime(options); /* inferred type Ably.Realtime */
let channel = client.channels.get(
"auctions"
);
and I could subscribe to that channel and update auctions accordingly by their id inside ngOnInit()
channel.subscribe(message => {
const auction = this.posts.find(action => {
return action.id === message.data.auctionId;
});
if (auction) {
auction.currentBid = message.data.lastBid;
}
});
but I need to switch this logic for JWT and somehow feed that JWT token into different components as well.
Ably.io JWT tutorial reference
I put the following in my angular login service
login(email: string, password: string) {
const authData: AuthDataLogin = { email: email, password: password };
return this.http
.post<{
token: string;
expiresIn: number;
userId: string;
}>(environment.azure_function_url + "/POST-Login", authData)
.pipe(takeUntil(this.destroy)).subscribe(response => {
//JWT login token. Not Ably JWT Token
const token = response.token;
this.token = token;
if (token) {
console.log('Fetching JWT token from auth server')
var realtime = new Ably.Realtime({
authUrl: "http://localhost:7071/api/AblyAuth"
});
realtime.connection.once('connected', function () {
console.log('Client connected to Ably using JWT')
alert("Client successfully connected Ably using JWT auth")
});
...
}
With my azure function already configured, When I login, the browser console outputs
GET wss://realtime.ably.io/?access_token=<token was here>&format=json&heartbeats=true&v=1.1&lib=js-web-1.1.22
SO it returns my token, but
the alert never happens
I'm not sure how to grab that JWT token that's returned to the browser. I was thinking I could store it in localStorage to share between components and clear out localStorage when user logs out, but I need to be able to subscribe to response and assign the token to a variable, but I didn't see in ably javascript tutorial how to get variable assigned to JWT Token response since it's being called with this syntax.
I appreciate any help with this!
var realtime = new Ably.Realtime({
authUrl: "http://localhost:7071/api/AblyAuth"
});
My azure function looks like
const checkAuth = require('../middleware/check-auth');
var jwt = require("jsonwebtoken")
var appId = '<APP ID>'
var keyId = '<key ID>'
var keySecret = '<key secret>'
var ttlSeconds = 60
var jwtPayload =
{
'x-ably-capability': JSON.stringify({ '*': ['publish', 'subscribe'] })
}
var jwtOptions =
{
expiresIn: ttlSeconds,
keyid: `${appId}.${keyId}`
}
console.log("JwtPayload");
console.log(jwtPayload);
console.log("jwtOptions");
console.log(jwtOptions);
module.exports = function (context, req) {
console.log("INSIDE ABLY AUTH")
// checkAuth(context, req);
console.log('Sucessfully connected to the server auth endpoint')
jwt.sign(jwtPayload, keySecret, jwtOptions, function (err, tokenId) {
if (err) {
console.log("ERR")
console.log(err)
console.trace()
return
}
context.res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
context.res.setHeader('Content-Type', 'application/json')
console.log('Sending signed JWT token back to client')
console.log(tokenId)
context.res = {
status: 200,
body: JSON.stringify(tokenId),
headers: {
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Set-Cookie",
"Access-Control-Max-Age": "86400",
"Vary": "Accept-Encoding, Origin",
"Content-Type": "application/json"
}
};
context.done();
})
}
I'd recommend if you're wanting to intercept the JWT prior to passing it to Ably (so as to verify the contents, and also use the JWT for other components), you make use of authCallback instead of authUrl. You can use a function instead of a direct URL, within which you can call the endpoint, and do anything you like with the response, prior to passing the JWT back to the Ably constructor. I've made a JavaScript example of using the authCallback for normal Token Authentication, but the same principle applies.
As to why you're not seeing the alert, it looks like you're sending an invalid JWT for what Ably is expecting, and thus you're not successfully connecting to Ably. For example, you're specifying 'expiresIn' rather than 'exp'. For a token to be considered valid, it expected certain elements in a very specific structure, see the documentation. I'd recommend for this sort of situation where you're not certain what's breaking that you make use of verbose logging, which you can enable in the connection constructor as "log": 4.

Request HTTP libraries are slow

I'm making a project using Node.js (8.9.4) and Express (4.16.2). Basically, with Node.js i'm consulting an external API and then with Express make a route to consume this result. My surprise was that when I used Axios library, it make my response to delay up to ~30s. I wanted to check if it was my problem or was from the API... I've checked it with PostMan and it returns in less than 300ms. Then I've thought it was any problem related with Axios, so I've decided to use request-promise but...again 30s. The last test I've made is using Node.js native 'https' util and...yes, less than 300ms again.
Anyone knows whats the problem with those packages? Am I condemned to use callbacks instead of promises?
Here's my base code... (AXIOS, Request-Promise... 30s delay in response)
const rp = require('request-promise);
const BASE_URL = 'my https url';
const AUTH_TOKEN = 'my auth token';
const options = {
uri: BASE_URL + '/my-route',
qs: { myQS: true },
headers: { authorization: AUTH_TOKEN }
method: 'GET'
};
rp(options)
.then(response => response)
.catch(error => error);
Here's my base code with HTTPS.... 300ms delay in response
const https = require('https');
const AUTH_TOKEN = 'my auth token';
const options = {
hostname: 'my hostname',
port: 443,
path: 'my path',
headers: { authorization: AUTH_TOKEN },
method: 'GET'
};
https.get(options, (res) => {
res.on('data', d => d);
};
Just a guess, but sounds like Axios is going via a different route in your network. Are both requests configured to use the same proxy setup?

Axios HTTP requests returns into an error (Access-Control-Allow-Origin)

I'm trying to make http post requests with Axios in JavaScript. The request was working fine, but then I tried to use cookies. As my backend I'm using an Express/Nodejs Server on http://localhost:8000, while my frontend is a react npm test server on http://localhost:3000.
My backend looks like this:
const express = require('express');
const cookieparser = require('cookie-parser');
const cors = require('cors');
const app = express();
app.use(cookieparser());
app.use(cors());
app.post("/request/status/check", (req, res) => {
if(req.cookies.gitEmployee != null){
res.status(200).send({res: 1, employeeName: req.cookies.gitEmployee.username, fullname: req.cookies.gitEmployee.fullname});
} else if(req.cookies.gitCompany != null){
res.status(200).send({res: 2, companyName: req.cookies.gitCompany.companyName, fullname: req.cookies.gitCompany.fullname});
}else{
res.status(200).send({res: 0});
}
});
app.post("/request/testcookie", (req, res) => {
res.cookie("gitEmployee", null);
res.cookie("gitEmployee", {
username: "testusername",
fullname: "Test Username"
}).send({res: 1});
});
So, as a short description: I'm setting a test cookie by posting a request to http://localhost:8000/request/testcookie. The response should be an JSON object where res = 1. Also, I'm trying to get information out of the cookie by posting a request to http://localhost:8000/request/status/check. In this case the response should be the object {res:1 , employeeName: "testusername", fullname: "Test Username"}.
I tried this concept with a REST Client called Insomnia (something like Postman) and it worked perfectly.
Then I wrote a helper-class for my React Application and for the Http request I'm using Axios.
import axios from 'axios';
class manageMongo {
authstate(){
return new Promise((resolve, reject) => {
axios("http://localhost:8000/request/status/check", {
method: "post",
data: null,
headers: {
"Access-Control-Allow-Origin": "*"
},
withCredentials: true
})
.then(res => {
console.log(res.data);
if(res.data.res === 0){
resolve(false);
}
if(res.data.res === 1){
resolve(true);
}
if(res.data.res === 2){
resolve(true);
}
});
});
}
setTestCookie(){
axios("http://localhost:8000/request/testcookie", {
method: "post",
data: null,
headers: {"Access-Control-Allow-Origin": "*"},
withCredentials: true
})
.then(res => { console.log(res)});
}
}
export default manageMongo.prototype;
When I execute these functions, I'm getting the same error of both of them (of course with different urls):
Failed to load http://localhost:8000/request/testcookie: Response to
preflight request doesn't pass access control check: The value of the
'Access-Control-Allow-Origin' header in the response must not be the
wildcard '*' when the request's credentials mode is 'include'
I already know that it's because of the withCredentials setting in the requests. I added these settings because I want to pass cookies through these requests and if I don't add withCredentials, the /request/status/check request always returns {res: 0} even if I set a cookie before.
I don't know, if this will change if the I set withCredentials = true but i read that in multiple threads. If you know an other working method to pass cookies through these requests even without axios please share it here! Because that is, what I want to achieve.
The problem seems to be you have set
'Access-Control-Allow-Origin': *
Try setting it to your actual origin, for example
'Access-Control-Allow-Origin': 'http://localhost:8000'
or
'Access-Control-Allow-Origin': 'http://localhost:3000'
Whichever the request originates from.

Seneca-web timeout configuration

First of all I would like to say that I am new in senecajs.
I am testing this configuration.
I have configured Senecjs microservice running on port 9007, which is running and handling request correctly. When I request this service directly I receive response after cca 10s (it is request for oracle db data).
But when I request for same data but through the Hapi + Seneca-web I receive this error: "statusCode":504,"error":"Gateway Time-out"
["client","invalid_origin",{"port":9007,"pin":"mc:bankgtw","pg":"mc:bankgtw","type":"web","id":"pg:mc:bankgtw,pin:mc:bankgtw,port:9007","role":"transport","hook":"client","plugin$":{"name":"client$"},"fatal$":true,"meta$":{"mi":"wbn8u45tb7uh","tx":"o3f8eyia3f4n","id":"wbn8u45tb7uh/o3f8eyia3f4n","pattern":"hook:client,role:transport,type:web","action":"(q1yytemztu3k)","plugin_name":"transport","plugin_tag":"-","prior":{"chain":[],"entry":true,"depth":0},"start":1487199713842,"sync":true},"tx$":"o3f8eyia3f4n","host":"0.0.0.0","path":"/act","protocol":"http","timeout":5555,"max_listen_attempts":11,"attempt_delay":222,"serverOptions":{}},{"kind":"res","res":null,"error":{"isBoom":true,"isServer":true,"output":{"statusCode":504,"payload":{**"statusCode":504,"error":"Gateway Time-out**","message":"Client request timeout"},"headers":{}}},"sync":true,"time":{"client_recv":1487199799177}}]
A few seconds before microservice return data.
And this is my configuration:
const Hapi = require('hapi');
const Seneca = require('seneca');
const SenecaWeb = require('seneca-web');
const config = {
adapter: require('seneca-web-adapter-hapi'),
context: (() => {
const server = new Hapi.Server();
server.connection({
port: 3001,
routes: {
cors: true,
payload:{timeout:60000},
timeout:{server: 60000, socket:90000}
}
});
server.route({
path: '/routes',
method: 'get',
handler: (request, reply) => {
const routes = server.table()[0].table.map(route => {
return {
path: route.path,
method: route.method.toUpperCase(),
description: route.settings.description,
tags: route.settings.tags,
vhost: route.settings.vhost,
cors: route.settings.cors,
jsonp: route.settings.jsonp,
server: server.info
}
})
reply(routes)
}
});
return server;
})()
};
const seneca = Seneca({timeout: 99999})
.use(SenecaWeb, config)
.use(require('./hapi_api.js'))
.client({ port:9007, pin:'mc:bankgtw' })
.ready(() => {
const server = seneca.export('web/context')();
server.start(() => {
server.log('server started on: ' + server.info.uri);
});
});
What I am doing wrong or what timeout is causing this?
I've had the same issue, fixed it, but its VERY BAD PRACTICE.
Go to 'transport.js' at seneca-transport folder.
You will see 'timeout: 5555'
Go ahead and change that to whatever you need.
I'm not sure why this is not getting USER defaults.
To the best of my knowledge, this is referring to client timeout. make sure you still use server timeout.

Categories