GraphQL mutation not being sent to request when using Apollo client? - javascript

I had an issue where I couldn't specify URI when sending custom headers to my backend server with { ApolloClient } from 'apollo-boost',
So I had to use { ApolloClient } from 'apollo-client' instead.
That issue was fixed, but now my mutations aren't being sent to the backend?
My mutation :
import { gql } from 'apollo-boost';
export const LOGIN_USER = gql`
mutation($email: String!, $password: String!) {
loginUser(email: $email, password: $password) {
userId
token
expiresIn
}
}
`
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
const httpLink = new HttpLink({
uri: 'http://localhost:3001/graphql'
})
const authLink = setContext((_, { headers }) => {
const store = JSON.parse(sessionStorage.getItem('interdevs-data'));
const token = store.token;
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
const login = async (email, password) => {
try {
const user = await loginUser({
variables: {
email,
password
}
});
const { userId, token, expiresIn } = user.data.loginUser;
setUserData({
token: token,
userId: userId,
expiresIn: expiresIn
});
sessionStorage.setItem('interdevs-data', JSON.stringify({
"token": token,
"userId": userId,
"expiresIn": expiresIn
}));
} catch(err) {
console.log('login error: ', err);
setLoginErr(err);
};
};
This is the error I'm getting.
"Error: Network error: Cannot read property 'token' of null"
When I switch it back importing ApolloClient from apollo-boost it works again.
Any help greatly appreciated!

Not 100% sure, but I think the error lies here:
const store = JSON.parse(sessionStorage.getItem('interdevs-data'));
const token = store.token;
If there are no items with the key interdevs-data, store will be null.
I think you can fix it by doing this:
const store = JSON.parse(sessionStorage.getItem('interdevs-data'));
const token = store ? store.token : null;

found out how to set auth headers with apollo-boost
const client = new ApolloClient({
uri: 'http://localhost:3001/graphql',
request: operation => {
const ssData = JSON.parse(sessionStorage.getItem('data'));
operation.setContext({
headers: {
authorization: ssData ? `Bearer ${ssData.token}` : ''
}
});
}
});

Related

I have an issue with refresh tokens in react

I have an issue with refresh tokens in react and I am using axios interceptors for calling refresh token I am recieving refresh token from flask APIs and the timer of refresh token is 30sec
This is the frontpage of login:
This is the login page;
After login I am recieving this data from the api:
data reacieved: After 30sec I am recieving this error and I am not able to call refresh tokens again for getting data from api
This is my code:`
This is the code where I am creating axios instance and interceptors for req and res:
import axios from 'axios';
const instance = axios.create({
baseURL: "http://192.168.18.63:5000",
headers: { "Content-Type": "application/json" },
});
instance.interceptors.request.use(config => {
const accessToken = localStorage.getItem('access_token');
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
});
instance.interceptors.response.use(
response => {
return response;
},
error => {
// If the access token is expired, try to refresh it
if (error.response.data.status === 403 && error.response.data.msg === 'Access token expired') {
const refreshToken = localStorage.getItem('refresh_token');
// Send a request to the server to refresh the access token
return instance
.post('/AdminRefreshToken', {
refresh_token: refreshToken,
})
.then(response => {
// Save the new access token
localStorage.setItem('access_token', response.data.access_token);
// Retry the original request
const config = error.config;
config.headers.Authorization = `Bearer ${response.data.access_token}`;
return instance(config);
});
}
return Promise.reject(error);
}
);
export default instance;`
This is the code where I am fetching the data from axios instance:
import React, { useState, useEffect } from "react";
import Logout from "./logout";
import api from "./Services/Api";
function MyComponent() {
const [data, setData] = useState("");
`your text`
useEffect(() => {
api
.get("/AdminDetails")
.then((response) => {
console.log(response);
setData(response.data.data);
})
.catch((error) => {
console.log(error);
});
}, []);
return <>
<div>
<h4>email: {data.email}</h4>
<h4>first Name: {data.firstname}</h4>
<h4>last Name: {data.lastname}</h4>
<h4>user Name: {data.username}</h4>
<Logout/>
</div>
</>;
}
export default MyComponent;

Send jwt to API in request header from Reactjs frontend

I have a quick question. I am using Axios to send requests to the nodejs API, when I set the token in the request header the API returns "jwt must be provided". The API expects the token with a custom name attached to it - here's how far I've gotten.
Snippet of API code that sends the token on login:
const token = jwt.sign(
{
userID: result[0].userID,
firstName: result[0].firstName,
lastName: result[0].lastName,
email: result[0].email,
role: result[0].role,
// exp: Math.floor(Date.now() / 1000) + 60 * 60,
},
"HeyImaPrivateKeyyy"
);
res.json({ token });
console.log("Login Attempt", res.statusMessage, req.body);
} else {
res.status(400).send({ message: "Invalid credentials!" });
console.log("Login Attempt", res.statusMessage, req.body);
}
-- React code from here --
Response from API on successful login:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOjEsImZpcnN0TmFtZSI6IkNhbWVyb24iLCJsYXN0TmFtZSI6IkVyYXNtdXMiLCJlbWFpbCI6ImNhbWVyb25AY2xpZnRjb2xsZWdlLmNvbSIsInJvbGUiOiJzdXBlckFkbWluIiwiaWF0IjoxNjYzMzEzNTM2fQ.9R6vXn-5Vb5fj48eUJGPNUGnXMw9TXOjJCox7U36WMI"
}
Saving the token on successful login (React)
const login = async ({ email, password }) => {
const res = await api.post(
"/auth",
{
email: email, //varEmail is a variable which holds the email
password: password,
},
{
headers: {
"Content-type": "application/json",
Authorization: false,
},
}
);
const { from } = state || {};
let token = jwt(res.data.token);
setToken("x-auth-token", token); // your token
localStorage.setItem("x-auth-token", res.data.token);
localStorage.setItem("userLogged", true);
localStorage.setItem("name", token.firstName);
localStorage.setItem("role", token.role);
navigate("/auth/dashboard" || from.pathname, { replace: true });
};
Here is the React component that is trying to call the API:
const [count, setCount] = useState(null);
const token = localStorage.getItem("x-auth-token");
const studentCount = useEffect(() => {
const config = {
headers: { "x-auth-token": token },
"Content-type": "application/json",
};
api.get("/students/", {}, config).then((response) => {
setCount(response.data);
});
}, [token]);
if (!count) return null;
This is what the API is expecting on request:
export const teacher = (req, res, next) => {
const token = req.header("x-auth-token");
if (!auth && !token)
return res.status(401).send({ message: "Access denied." });
const decoded = jwt.verify(token, "DemoPrivateKey");
if (auth && ["superAdmin", "admin", "teacher"].includes(decoded.role)) {
next();
} else {
res.status(400).send({ message: "Access denied!" });
}
};
Ideally, I would like to send the token as a header on successful login, but it saves as undefined on the client (have no idea how to fix that).
If you're using Axios then, as per the doc, get method should have config parameter in second position not third one.
So maybe, simply updating api.get("/students/", {}, config) into api.get("/students/", config) should solve your issue.

JWT returned invalid signature on axios request reactjs

So im trying to create refreshtoken hook in react.
and nodejs with express as my backend.
my backend code looks like this
exports.refreshToken = (req, res) => {
const oldToken = req.headers.authorization.split(" ")[1]
if(oldToken == null ) return res.status(500).send({message: "Token is empty"})
console.log(myJwt.refreshSecretKey)
console.log(oldToken)
jwt.verify(oldToken, myJwt.refreshSecretKey, (err, user) => {
if(err)
res.status(500).send({
msg: err || "Error on refreshing your token"
})
else res.send({ refreshToken: generateRefreshToken() });
})
};
the problem is when i try this endpoint with Postwoman (chrome extension) its WORK
but when i try with React + axios the server return is
{"msg":{"name":"JsonWebTokenError","message":"invalid signature"}}
here is my react code
import axios from '../api/axios'
import useAuth from './useAuth'
const useRefreshToken = () => {
const Auth = useAuth()
const refresh = async () => {
console.log(Auth.auth.token)
const response = await axios.get("user/refresh", {
withCredentials: true,
headers: {
Authorization: `Bearer ` + Auth.auth.token
}
})
Auth(prev => {
console.log(JSON.stringify(prev))
console.log(response?.data?.refreshToken)
return {...prev, token: response.data.refreshToken}
})
return response.data.refreshToken
}
return refresh
}
export default useRefreshToken
I'm sending the wrong access token.
What I send in react is the first created accessToken. not the refreshAccessToken

React axios Interceptors: Authorization invalid token

Axios interceptors works really well for http://127.0.0.1:8000 local API calls. Here is the working code.
import axios from 'axios';
const http = axios.create({
baseURL: 'http://127.0.0.1:8000/api/',
Headers: {},
});
try {
http.interceptors.request.use(
(config) => {
let data = JSON.parse(localStorage.getItem('cyber-minds'));
if (data && data.user_status.token) {
config.headers['Authorization'] = 'Token ' + data.user_status.token;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
} catch (error) {
console.log(error);
}
export default http;
But after I deploy http://127.0.0.1:8000 to https://mysite-backend.herokuapp.com and replace my baseURL with https://mysite-backend.herokuapp.com it returns invalid token. here is the code which returns invalid token.
import axios from 'axios';
const http = axios.create({
baseURL: 'https://cyberminds-backend.herokuapp.com/api/',
Headers: {},
});
try {
http.interceptors.request.use(
(config) => {
let data = JSON.parse(localStorage.getItem('cyber-minds'));
if (data && data.user_status.token) {
config.headers['Authorization'] = 'Token ' + data.user_status.token;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
} catch (error) {
console.log(error);
}
export default http;
Here is local storage information.
{token: "e3746603ad6c8788b9936118f1fc36289bb20a8d", user: {id: 2,…},…}
assetRisk: "Low"
cid: 1
cpe: "cpe:2.3:a:oracle:peoplesoft_enterprise:8.22.14"
id: 2
name: ""
pid: 2
pr: "Windows"
token: "e3746603ad6c8788b9936118f1fc36289bb20a8d"
user: {id: 2,…}
user_status: {loggedIn: true, token: "e3746603ad6c8788b9936118f1fc36289bb20a8d"}
vendor: "Oracle"
The authentication is working very well. Authorization returns invalid token how can i resolve this issue?

node.js oauth-1.0a working for Twitter API v1.1 but not for v2

I've found this function to generate oauth-1.0a header:
// auth.js
const crypto = require("crypto");
const OAuth1a = require("oauth-1.0a");
function auth(request) {
const oauth = new OAuth1a({
consumer: {
key: process.env.TWITTER_API_KEY,
secret: process.env.TWITTER_API_SECRET_KEY,
},
signature_method: "HMAC-SHA1",
hash_function(baseString, key) {
return crypto.createHmac("sha1", key).update(baseString).digest("base64");
},
});
const authorization = oauth.authorize(request, {
key: process.env.TWITTER_ACCESS_TOKEN,
secret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
});
return oauth.toHeader(authorization).Authorization;
}
module.exports = auth;
It works fine if I try it with Twitter API v1.1:
// v1.js
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = "https://api.twitter.com/1.1/favorites/create.json";
const method = "POST";
const params = new URLSearchParams({
id: "1397568983931392004",
});
axios
.post(url, undefined, {
params,
headers: {
authorization: auth({
method,
url: `${url}?${params}`,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
But if I try it with Twitter API v2:
// v2.js
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = {
tweet_id: "1397568983931392004",
};
axios
.post(url, data, {
headers: {
authorization: auth({
method,
url,
data,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
it fails with:
{
title: 'Unauthorized',
type: 'about:blank',
status: 401,
detail: 'Unauthorized'
}
I tried encoding the body of the request as suggested here, but get the same error:
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const querystring = require("querystring");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = percentEncode(
querystring.stringify({
tweet_id: "1397568983931392004",
})
);
function percentEncode(string) {
return string
.replace(/!/g, "%21")
.replace(/\*/g, "%2A")
.replace(/'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29");
}
axios
.post(url, data, {
headers: {
"content-type": "application/json",
authorization: auth({
method,
url,
data,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
If tested with Postman, both endpoints (1.1 and 2) work fine with the same credentials.
Any ideas on what am I doing wrong while using v2 or how to get it working with Twitter API v2?
I suspect it's something related with the body of the request as that's the main diference between both requests, but haven't been able to make it work.
Figure it out, the body of the request should not be included while generating the authorization header:
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = {
tweet_id: "1397568983931392004",
};
axios
.post(url, data, {
headers: {
authorization: auth({
method,
url,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
Basically, when making a post request to Twitter API v1.1, the data should be encoded, should be used to generate the authorization header, and the post request should be sent as application/x-www-form-urlencoded.
When making a post request to Twitter API v2, the data should not be encoded, should not be included while generating the authorization header, and must be sent as application/json.
Hope this becomes helpful to someone else.

Categories