How can I send different request in intercepter? - node.js

I'm trying to send different request in interceptor
I want to send accessToken in request header authorization for every request except one case
so I write this code in interceptor request.use
config.headers = {authorization: `Bearer ${accessToken}`};
but if this error occurred
error.response.data.code === 'expired'
I want to send refreshtoken in header authorization not accesstoken
so I write this code in interceptor.response.use.error
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
this is my code
useEffect(() => {
axios.interceptors.request.use(async (config: any) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
config.headers = { authorization: `Bearer ${accessToken}` };
return config;
});
axios.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const {
config,
response: { status },
} = error;
if (status === 419) {
if (error.response.data.code === "expired") {
const originalRequest = config;
const refreshToken = await EncryptedStorage.getItem("refreshToken");
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
return axios(originalRequest);
}
}
return Promise.reject(error);
}
);
}, [dispatch]);
how can i fix my code?
if i use my code if error.response.data.code === 'expired'
the headers.authorization accesstoken is still being requested.

Make it so your request interceptor only sets a default authorization header without overriding anything already present
axios.interceptors.request.use(async (config) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
return {
...config,
headers: {
authorization: `Bearer ${accessToken}`,
...config.headers
}
}
});
You could also avoid making the getItem() request entirely which might save a little time
axios.interceptors.request.use(async (config) => {
if (!config.headers.authorization) {
config.headers.authorization = `Bearer ${await EncryptedStorage.getItem("accessToken")}`
}
return config;
});

Related

Implementation of rxjs BehaviourSubject in Next.js for state management not working

Trying to store jwt token on login using rxjs behavioursubject
Then creating a http request with Authorization: Bearer ${user.jwtToken} in the
I believe I need to have
a) initial value,
b) a source that can be turned into an observable
c) a public variable that can be subscribed
On log in the user is correctly added to the user subject here "userSubject.next(user);"
But whenever I try to create the bearer token its always null
// The Accounts Service
// initialise and set initial value
const userSubject = new BehaviorSubject(null);
const authApiUrl = "https:testApi";
export const accountService = {
` user: userSubject.asObservable(), get userValue() { return userSubject.value },
login,
getAllUsers
};
function login(email, password) {
return fetchWrapper.post(process.env.AuthApiUrl + '/accounts/authenticate', { email, password })
.then(user => {
userSubject.next(user);
localStorage.setItem('user', JSON.stringify(user));
return user;
});
}
function getAllUsers() {
return await fetchWrapper.get(process.env.AuthApiUrl + '/accounts/get-all-users');
}
}
// The fetchwrapper
export const fetchWrapper = {
get,
post
};
function post(url, body) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeader(url) },
credentials: 'include',
body: JSON.stringify(body)
};
return fetch(url, requestOptions).then(handleResponse);
}
function get(url) {
const requestOptions = {
method: 'GET',
headers: authHeader(url)
};
return fetch(url, requestOptions).then(handleResponse);
}
function authHeader(url) {
// return auth header with basic auth credentials if user is logged in and request is to the api url
// THE accountService.userValue IS ALWAYS NULL???
const user = accountService.userValue;
const isLoggedIn = user && user.jwtToken;
const isApiUrl = url.startsWith(process.env.AuthApiUrl);
if (isLoggedIn && isApiUrl) {
return { Authorization: `Bearer ${user.jwtToken}` };
} else {
return {};
}
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if ([401, 403].includes(response.status) && accountService.userValue) {
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
accountService.logout();
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}

How to receive re-requested data when data requested by mounted() is re-requested by aixos interceptor

the code below is the first request for get list data
mounted() {
this.getList()
},
methods: {
handleClick(row) {
console.log(row.id)
console.log(row.url)
this.$router.push({ name: 'Main', query: { id: row.id } })
},
getList() {
axios
.get('/api/v1/requests/all', {
params: {
userId: this.$store.state.userInfo.id,
},
})
.then(response => {
let moment = require('moment')
for (var item of response.data.data) {
item.createdAt = moment(item.createdAt).format(
'YYYY-MM-DD HH:mm:ss',
)
}
this.items = response.data.data
})
.catch(error => {
console.log(error)
})
}
my interceptor
axios.interceptors.response.use(
function (response) {
return response
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true
sessionStorage.removeItem('access-token')
let headers = {
grant_type: 'refresh_token',
Authorization: sessionStorage.getItem('refresh-token'),
}
axios
.post('/api/v1/users/refresh_token', {}, { headers: headers })
.then(response => {
let token = response.data.data
sessionStorage.setItem('access-token', token)
originalRequest.headers['Authorization'] = token
originalRequest.headers['grant_type'] = 'grant_type'
return axios.request(originalRequest)
})
.catch(error => {
console.log(error)
alert('blablabla.')
})
}
return Promise.reject(error)
},
)
the flow is i understand
1.token expired
2.move to list page
3.mounted hook is request data
4.getList -> axios get('~~request/all')
5.interceptor->axios post('~~~refresh_token')
6.re request with new token(request/all)
7.re request is 200, but not update list page
i'd really appreciate your help :)
Seems like you need to return the second request (await for result and return). Right now the result of second request seems to be ignored
axios.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true;
sessionStorage.removeItem("access-token");
let headers = {
grant_type: "refresh_token",
Authorization: sessionStorage.getItem("refresh-token"),
};
const [secondError, res] = await axios // await here
.post("/api/v1/users/refresh_token", {}, { headers: headers })
.then(async (response) => {
let token = response.data.data;
sessionStorage.setItem("access-token", token);
originalRequest.headers["Authorization"] = token;
originalRequest.headers["grant_type"] = "grant_type";
return [null, await axios.request(originalRequest)];
})
.catch((err) => {
console.log(err);
alert("blablabla.");
return [err, null];
});
// Handle here
if(secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
return Promise.reject(error);
}
);
The above solution worked for me perfectly. here's the modified code that I used for my requirement.
export default function axiosInterceptor() {
//Add a response interceptor
axios.interceptors.response.use(
(res) => {
// Add configurations here
return res;
},
async function (error) {
const originalRequest = error.config;
let secondError, res
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
[secondError, res] = await axios({
method: "POST",
url: `${baseURL}/account/token/refreshtoken`,
withCredentials: true,
headers: { "Content-Type": "application/json" },
})
.then(async (response) => {
//handle success
return [null, await axios.request(originalRequest)];
}).catch(function (error) {
console.error(error)
});
}
if (secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
);
}

How Can I listen for a REST-like endpoint in a Graphql resolver?

After successfully integrating a 3rd party REST Oauth Api, now i am trying to convert the api logic into a graphql query but i'm kind of stuck on how best to architect the query.
From a REST perspective, i have something like this that works 100%
app.get("/auth/redirect", async (req: Request, res:Response) => {
const code = req.query.code
const { access_token, account_id } = await getTokens({
code,
clientId,
clientSecret,
})
try{
const result = await axios
.get(
`https://api.specificservice.dev/helloworld/id/v1/accounts?accountId=${account_id}`,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Bearer ${access_token}`,
},
}
)
console.log('Fetching user..', result.data)
return result.data
}
catch (error: any) {
let message = error.result.data
console.log(message)
return message
}
})
from Graphql, I've got a sketch like this.
getPlatform: async (_: any, _args: any, { req }: any) => {
const clientId = "test"
const clientSecret = "test"
const code = req.query.code
const { access_token, account_id } = await getTokens({
code,
clientId,
clientSecret,
})
try{
const result = await axios
.get(
`https://api.specificservice.dev/helloworld/id/v1/accounts?accountId=${account_id}`,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Bearer ${access_token}`,
},
}
)
console.log('Fetching user..', result.data)
return result.data
}
catch (error: any) {
let message = error.result.data
console.log(message)
return message
}
},
How can I refactor this to suit what I'm trying to do from the rest endpoint

Cannot get user id - 404 User not found

When I navigate to the user/dashboard, I should see the user id at the end of the url but instead I see http://localhost:3000/profile/$%7B_id%7D no matter who is signed in and I get a 404 error with the response: error: "User not found". I don't know where $%7B_id%7D is coming from.
What do I need to change in my code to fix this and get the correct user id?
export const isAuthenticated = () => {
if (typeof window == 'undefined') {
return false;
}
if (localStorage.getItem('jwt')) {
return JSON.parse(localStorage.getItem('jwt'));
} else {
return false;
}
};
apiUser.js
import { API } from "../config";
export const read = (userId, token) => {
return fetch(`${API}/user/${userId}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
}
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
export const update = (userId, token, user) => {
return fetch(`${API}/user/${userId}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify(user)
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
export const updateUser = (user, next) => {
if (typeof window !== "undefined") {
if (localStorage.getItem("jwt")) {
let auth = JSON.parse(localStorage.getItem("jwt"));
auth.user = user;
localStorage.setItem("jwt", JSON.stringify(auth));
next();
}
}
};
UserDashboard.js
const Dashboard = () => {
const {
user: { _id, name, email, role }
} = isAuthenticated();
const userLinks = () => {
return (
<Link className="nav-link" to="/profile/${_id}">
Update Profile
</Link>
);
};
the problem is that this function:
export const isAuthenticated = () => {
if (typeof window == 'undefined') {
return false;
}
if (localStorage.getItem('jwt')) {
return JSON.parse(localStorage.getItem('jwt'));
} else {
return false;
}
};
returns a boolean
but here:
const {
user: { _id, name, email, role }
} = isAuthenticated();
you are trying to abstract _id out of it and it doesn't know what _id is. so you need to make sure that it returns an object with these keys if you're going to destructure
I forgot to use template strings and {} when using _id here: to="/profile/${_id}">
Changed to: to={`/profile/${_id}`}> and _id is working.

Why can't I get a usable response from graphql with axios?

I'm successfully authenticating with the server, sending my graphql request and receiving a response. However the response isn't quite right.
This is just a simple nodejs, axios, graphql situation. I can get a correct respose via postman or curl but not with node. I've tried fetch as well to no avail. Am I doing something wrong? This is for the producthunt api, the api explorer is located at here
require('dotenv').config()
const axios = require("axios");
const PH_KEY = process.env.PH_KEY;
const PH_SECRET = process.env.PH_SECRET;
const BASE_URL = process.env.BASE_URL;
const AUTH_URL = process.env.AUTH_URL;
const GRAPHQL_URL = process.env.GRAPHQL_URL;
const PH = axios.create({
baseURL: BASE_URL,
responseType: 'json',
headers: {
// 'Accept': 'application/json, text/plain, gzip, deflate, */*',
'Content-Type': 'application/json'
},
})
let TOKEN = 'Bearer '
const getAuth = async () => {
return await PH({
url: AUTH_URL,
method: "post",
data: {
client_id: `${PH_KEY}`,
client_secret: `${PH_SECRET}`,
grant_type: "client_credentials"
}
})
.then((res) => {
console.log('auth status ', res.status)
return res.data.access_token
})
.then((res) => {
TOKEN += res
console.log(TOKEN)
})
.catch((err) => {
console.log(err)
})
}
const getHunt = async () => {
await getAuth()
PH.defaults.headers.common['Authorization'] = TOKEN
return await PH({
url: GRAPHQL_URL,
method: 'post',
data:
{
query: `
query posts {
posts(order: RANKING, first: 1) {
edges {
node {
name
votesCount
user {
name
}
createdAt
}
}
}
}
`
}
})
.then((res) => {return res})
.then((res) => {
console.log(res.data)
return res.data
})
.catch((err) => {
console.log(err)
})
}
const Main = async () => {
await getHunt()
}
Main()
This is the output I receive in node:
[Running] node "/Users/fireinjun/Work/YAC/yac-ph-tophuntfunction/app.js"
auth status 200
Bearer #################################################
{ data: { posts: { edges: [Array] } } }
Here's what I'm expecting:
{
"data": {
"posts": {
"edges": [
{
"node": {
"name": "Trends by The Hustle",
"votesCount": 125,
"user": {
"name": "Jack Smith"
},
"createdAt": "2019-06-04T07:01:00Z"
}
}
]
}
}
}
I was accessing the data incorrectly! Apparently I needed to see the res.data.data.posts.edges[0]

Resources