GDMS (Grandstream API) POST request with axios - node.js

I am trying to make a POST request to the GDMS api.
I keep getting the error code: 4000, which means "signature not exists"
I first obtain the token and return axios.create()
exports.client = async () =>{
return new Promise((resolve =>{
axios.get('https://www.gdms.cloud/oapi/oauth/token', {
params:{
username: username,
password: password,
grant_type: grant_type,
client_id: client_id,
client_secret: client_secret
}
}).then(res=>{
let client = axios.create({
baseURL: 'https://www.gdms.cloud/oapi/v1.1.0',
headers: {'authorization': `bearer${res.data.access_token}`}
})
return resolve(client)
}).catch(error=>{
console.log(error.message)
return resolve(false)
})
}))
}
That works fine.
Then I call the endpoint /sip/servers/list like so
await gdms.sipServers.list(client)
exports.list = async (client) =>{
return new Promise(resolve => {
let timestamp = new Date().toISOString()
client.post('/sip/server/list',{
timestamp: timestamp,
signature: sha256(`&timestamp=${timestamp}&`)
}).then(res=>{
console.log(res.data)
return resolve(res.data)
}).catch(error=>{
console.log(error.message)
return resolve(false)
})
})
}
But I keep getting the same error "signature not exists"
Any help would be greatly appreciated.
This is the documents for the signature
https://doc.grandstream.dev/GDMS-API/EN/#api-157061470296601000002

Related

Why is req.body empty {} (GET REQUEST)

This is my Frontend code
const fetchData = () => {
const options = {
method: 'GET',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: [chartData.datasets]
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
This is backend
app.get('/user/chart', async (req, res) => {
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})
When I console.log(req.body) it is an empty {}.
Why is it empty?
I am using a GET request to retrieve the chart data
Axios API does not accept body on get get request you can send parameters with params example
const url = '/user/chart';
const config = {
headers: {'x-access-token': sessionStorage.getItem('token')},
params:{someKey:chartData.datasets}
};
axios.get(url, config)
Axios doesn't support setting a body for a get request, see the docs or this related question.
Though, I'd also recommend to reconsider your design. Typically the body isn't used in a GET request. If you're sending data to the server, you likely want to use POST or PUT instead. If you just want to pass a parameter, then you likely want to use request parameters.
If you absolutely need to send a body in your GET request, then you'll need to use a different tool.
frondend //
const fetchData = () => {
const options = {
method: 'POST',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: {name : "xx",mail:"xx#"}
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
backend //
app.post('/user/chart', async (req, res) => {
const {name , mail} = req.body
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})Ï

How to catch error of rejected promise on client side?

I am trying to have my server reject the signup request if the user tries to sign up with an existing account. However, I cant seem to reject it properly and pass the error message to my client side.
//server.js
app.post('/signup', (req, res) => {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
User.find({ email: email }, (err, existingUser) => {
//account doesnt exist
if (existingUser.length === 0) {
bcrypt.hash(plainTextPassword, saltRounds, async (err, hash) => {
try {
const user = new User({
email: email,
password: hash
});
let result = await user.save();
if (result) {
res.send(result)
}
} catch (e) {
res.send(e);
}
})
} else {
//notify user that account exists
return Promise.reject(new Error('Account already exists'))
}
})
})
//reduxSlice.js
export const signup = createAsyncThunk(
'userAuth/signup',
async (payload, thunkAPI) => {
const { email, password } = payload
try {
const result = await fetch(
signupPath, {
mode: 'cors',
credentials: 'include',
method: "post",
body: JSON.stringify({ email, password }),
headers: {
'Content-Type': 'application/json'
}
}
)
return result.json()
} catch (error) {
console.log(error) //this line executes
}
}
)
From my reduxdev tools, my signup is still fulfilled EVEN though I rejected it from my server. Also, my server crashes after one attempt, which leads me to suspect there is an uncaught error.
The client only receives what you do res.send() with or next(err) which will then call res.send(). Promises are local only to the server and are not something that gets sent back to the client.
In your original code, I'd suggest that you use ONLY promise-based asynchronous operations and then you can throw in your code, have one place to catch all the errors and then send an error back to the client from there.
class ServerError extends Error {
constructor(msg, status) {
super(msg)
this.status = status;
}
}
app.post('/signup', (req, res) => {
try {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
const existingUser = await User.find({ email: email });
//account doesnt exist
if (existingUser.length !== 0) {
throw new ServerError('Account already exist', 403);
}
const hash = await bcrypt.hash(plainTextPassword, saltRounds);
const user = new User({
email: email,
password: hash
});
const result = await user.save();
res.send(result);
} catch (e) {
if (!e.status) e.status = 500;
console.log(e);
res.status(e.status).send({err: e.message});
}
});
Then, in your client code that is using fetch(), you need to check result.ok to see if you got a 2xx status back or not. fetch() only rejects if the network connection to the target host failed. If the connection succeeded, even if it returns an error status, the fetch() promise will resolve. You have to examine result.ok to see if you got a 2xx status or not.
//reduxSlice.js
export const signup = createAsyncThunk(
'userAuth/signup',
async (payload, thunkAPI) => {
const { email, password } = payload
try {
const result = await fetch(
signupPath, {
mode: 'cors',
credentials: 'include',
method: "post",
body: JSON.stringify({ email, password }),
headers: {
'Content-Type': 'application/json'
}
});
// check to see if we got a 2xx status success
if (!result.ok) {
throw new Error(`signup failed: ${response.status}`);
}
return result.json()
} catch (error) {
console.log(error) //this line executes
}
}
)

Set-Cookie not sended on HTTP response header

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.

How to display the back-end error message instead of status code

I created a Backend server and posted it to Heroku and now I am using the server URL to post and get data from it. However when I display an error message it's getting me the status code instead of the actual error.
My Backend login code.
export const loginUser = asyncHandler(async(req, res) => {
const { email, password } = req.body;
const user = await userModel.findOne({ email });
const token = generateToken(user._id)
if(user && (await user.matchPassword(password))){
res.json({
_id:user._id,
username: user.username,
email: user.email,
profilePic:user.profilePic,
admin:user.admin,
token:token,
});
} else {
res.status(400)
throw new Error("invalid email or password please check and try again!");
}
});
My user Actions code since I am suing redux
export const login = (email, password) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_REQUEST,
});
const url = `${process.env.REACT_APP_SERVER_URL}api/loginuser`;
const config = {
headers: {
"Content-type": "application/json",
},
};
const { data } = await axios.post(
url,
{
email,
password,
},
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
Error in frontend display
First you need to send an error message from your Back End like so:
export const loginUser = asyncHandler(async(req, res) => {
const { email, password } = req.body;
const user = await userModel.findOne({ email });
const token = generateToken(user._id)
if(user && (await user.matchPassword(password))){
res.json({
_id:user._id,
username: user.username,
email: user.email,
profilePic:user.profilePic,
admin:user.admin,
token:token,
});
} else {
res.status(400).json({errorMessage :"invalid email or password please check and try again!" })
}
});
Then get it in the React part (make sure to read the comment I added after that console.log):
export const login = (email, password) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_REQUEST,
});
const url = `${process.env.REACT_APP_SERVER_URL}api/loginuser`;
const config = {
headers: {
"Content-type": "application/json",
},
};
const { data } = await axios.post(
url,
{
email,
password,
},
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
} catch (error) {
console.log(error); // look at the console, as I may miss the correct retuned error object
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response.data.errorMessage
});
}
};
you need to return response instead of throwing the error:
res.status(400)
res.json({
message: "invalid email or password please check and try again!"
});

How to fetch a specific field from axios post API request

I am using the below piece of code
const axios = require('axios')
axios
.post('https://xxx', {
"audience": "http://xxxx",
"grant_type": "xxxxx",
"client_id": "xxxxx",
"client_secret": "xxxxx"
})
.then(res => {
console.log(res)
})
.catch(error => {
console.error(error)
})
And I wanted assign the "res.data.token" to a variable token and use the variable in below code
describe('/GET device information', function () {
it("it should GET a Good Auth Status", function(done) {
chai.request('http:xxxxxx')
.get('xxxxxxxxxxxx')
.set({ "Authorization": `Bearer ${token}` })
.then((res) => {
(res).should.have.status(200);
// console.log(body) - not really needed, but I include them as a comment
done();
}).catch((err) => done(err))
});
})
you could wrap it in a try/catch and destructure the object:
try {
const res = await axios.post('https://xxx', {
'audience': 'http://xxxx',
'grant_type': 'xxxxx',
'client_id': 'xxxxx',
'client_secret': 'xxxxx'
})
const { data, token, foo, bar, status } = res.data
(status).should.equal(200)
} catch(e) {
console.log(e)
}
}
quick example

Resources