Hi I'm setting default axios headers in request interceptor but these headers are not accessible in another function... in axios axios documentation it is mentioned that global-axios-defaults are global...below is my sample code need help
axios.interceptors.request.use(function (config) {
axios.defaults.headers.accesstoken= "some_access_token"
axios.defaults.headers.client = "some_client"
axios.defaults.headers.uid = "some_uid"
return config;
},function (error) {
return Promise.reject(error);
});
On page load componentDidmount executes but axios default headers are undefined in this function
componentDidMount: function() {
console.log(axios.defaults.headers) #its giving me undefined
axios.get("http://some_url_for_get_request.json", {
headers: {
accesstoken: axios.defaults.headers.accesstoken,
uid: axios.defaults.headers.uid,
client: axios.defaults.headers.client
}
})
}
You can set the default Custom Headers in Axios for every XHR call like this:
axios.defaults.headers.common = {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": "example-of-custom-header"
};
You can also add configurations onward like this:
window.axios.defaults.headers.post['xsrfCookieName'] = 'CSRFToken';
window.axios.defaults.headers.post['xsrfHeaderName'] = 'X-CSRFToken';
window.axios.defaults.headers.post['responseType'] = 'json';
window.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
See the global options here (Request Config)
Also, you can create a configuration passed into an instance.
See more: here (Axios Create Config)
on your MAIN.JS
import axios from "axios";
const base = axios.create({
baseURL: "http://127.0.0.1:8000/",
});
Vue.prototype.$http = base;
Vue.prototype.$http.interceptors.request.use(
config => {
let accessToken = localStorage.getItem('token');
if (accessToken) {
config.headers = Object.assign({
Authorization: `Bearer ${accessToken}`
}, config.headers);
}
return config;
},
error => {
return Promise.reject(error);
}
);
Related
I'll do my best to explain my issue.
I'm building a NextJS site and using Apollo to handle Graph QL, and it's been working perfectly fine when I test the API using Postman, but when I try to run in dev I get CORS errors:
This operation has been blocked as a potential Cross-Site Request Forgery (CSRF). Please either specify a 'content-type' header (with a type that is not one of application/x-www-form-urlencoded, multipart/form-data, text/plain) or provide a non-empty value for one of the following headers: x-apollo-operation-name, apollo-require-preflight
This is my code that runs the Apollo code.
index.ts:
import { ApolloServer } from '#apollo/server';
import { startServerAndCreateNextHandler } from '#as-integrations/next';
import { applyMiddleware } from 'graphql-middleware';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { getUserFromToken } from './../../../server/security/jwt';
import typeDefs from './schema';
import { resolvers } from './resolvers';
import { permissions } from './permissions';
const schema = applyMiddleware(
makeExecutableSchema({
typeDefs: typeDefs,
resolvers,
}),
permissions
);
const server = new ApolloServer({
schema,
includeStacktraceInErrorResponses: false,
});
export default startServerAndCreateNextHandler(server, {
context: async (req, res) => {
const cookies = req.cookies;
const token = cookies.titanAuthToken || '';
var user;
if (token) {
user = getUserFromToken(token);
}
return { req, res, user, token };
},
});
I have tried a few suggestions from google including this:
const server = new ApolloServer({
schema,
includeStacktraceInErrorResponses: false,
cors: {
origin: true,
credentials: true, // true if you need cookies/authentication
methods: ['GET', 'POST', 'OPTIONS'],
}
});
But when I try the above I get this error:
Argument of type '{ schema: GraphQLSchemaWithFragmentReplacements; includeStacktraceInErrorResponses: false; cors: { origin: boolean; credentials: boolean; methods: string[]; }; }' is not assignable to parameter of type 'ApolloServerOptions<BaseContext>'.
Object literal may only specify known properties, and 'cors' does not exist in type 'ApolloServerOptionsWithSchema<BaseContext>
Any help to fix this CORS error would be greatly appreciated.
I am trying to set up authentication for a project. Once a user signs up for our app they get sent to our home page with an id in the query. This id then gets used to submit user and then the jwt token gets saved inside redux state.
All our calls now go through an axios client where the jwt token is passed on every request. The token gets read with store.getState(injectStore)
This all works fine inside getserversideProps, but the issue comes in when using calls on the frontend that goes through NextJs built in 'pages/api' folder. Any calls inside those folders causes the store.getState() to be undefined. I do not understand why since it uses the exact same client as geserversideProps.
Example GetServersideProps(working)
try {
const response = await serverApiClient.get('v1/config');
return {
props: {
},
};
} catch ({ error: { statusCode = 500, message = 'Internal Server Error' } }) {
if (statusCode === 401) {
return {
redirect: {
permanent: false,
destination: '/',
},
};
}
throw new Error(message as string);
}
};
Example Frontend bff call(not working)
try {
// Call below get sent to next built in api
const players = await apiClient.get(`/defenders?sortBy=${statId}&team_id=${teamShortName}`);
return players;
} catch (error) {
return { error };
}
};
export default async function handler(req: NextApiRequest) {
console.log('Start request')
try {
const { sortBy, team_id: teamId } = req.query;
const response = await serverApiClient.get(`/v1/players/picks?position=DEF&sort_by=${sortBy}&team_id=${teamId}`);
Api Client
mergeConfigs(
params: Record<string, string>,
headers: Record<string, string>,
configs: Record<string, string>,
): AxiosRequestConfig {
const defaultConfigs = ApiClient.getDefaultConfigs();
*const token = store?.getState()?.jwtToken?.value*
//ISSUE ABOVE - This store .getState() is only undefined in nextJS api folder calls.
return {
...defaultConfigs,
...configs,
params,
headers: {
...defaultConfigs.headers,
...headers,
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
};
}
get(
uri: string,
params = {},
headers = {},
configs = {},
): Promise<AxiosResponse | any> {
return this.client
.get(uri, this.mergeConfigs(params, headers, configs))
.then((response) => {
return (response.data ? response.data : response);
})
.catch((error) => {
const errorObject = {
error: error?.response?.data,
};
throw Object.assign(errorObject);
});
}
If anyone has some advice on why that getStore is undefined in frontend-to-backend calls please assist. Thanks all!
I have implemented authorization using cookies, when ApolloClient is initialized, an authorization link is specified, where there is a token in the headers. But when trying to load data with getServerSideProps I always get an Unauthorized error even though without getServerSideProps everything works. This is my first experience with apollo, so I don't quite understand how to fix it.
My ApolloClient:
import { parseCookies } from 'nookies'
import { setContext } from '#apollo/client/link/context';
import { ApolloClient, InMemoryCache, createHttpLink } from '#apollo/client'
const cookies = parseCookies()
const httpLink = createHttpLink({
uri: 'http://localhost:3001/graphql',
});
const authLink = setContext((_, { headers }) => {
const token = cookies.access_token
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
export let client = new ApolloClient({
cache: new InMemoryCache(),
credentials: 'same-origin',
link: authLink.concat(httpLink),
})
Component with getServerSideProps:
export default function Suggestions({ users }: any) {
return (<MainLayout>
{JSON.stringify(users)}
</MainLayout>)
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
const { data } = await client.query({
query: GET_USERS
});
return {
props: { users: data.users },
}
}
Main.js file on the backend (backend on Nestjs)
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, { cors: true });
app.useGlobalPipes(new ValidationPipe())
app.useGlobalGuards(new JwtAuthGuard(new Reflector()))
app.enableCors({
origin: 'http://localhost:3000',
credentials: true,
allowedHeaders: 'Origin,X-Requested-With,Content-Type,Accept,Authorization,authorization'
})
await app.listen(3001);
}
bootstrap();
And Graphql in App.module on backend
#Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: path.join(process.cwd(), 'src/schema/gql'),
sortSchema: true,
driver: ApolloDriver,
context: ({ req, res }) => ({ req, res }),
cors: {
origin: 'http://localhost:3000',
credentials: true,
}
}),
I have been trying to obtain data using Axios through Redux-saga using Redux-toolkit & react. It appears that intercepting a saga call with a token gets redux-saga in an infinite loop? Or is it because of my watchers?
I have recently been learning how to program so my skills in all areas are not yet great, hope you dont mind the way the code is written as I have been following tutorials mostly.
On handleSubmit from a Header.tsx to dispatch
const handleSubmit = (e) => {
e.preventDefault();
dispatch(getCurrentUser());
};
my rootSaga.tsx includes all watcherSagas notices the dispatch for getCurrentUser()
import { takeLatest } from "redux-saga/effects";
import {
handleLogInUser,
handleGetCurrentUser,
handleSetCurrentUser,
} from "./handlers/user";
import {
logInUser,
getCurrentUser,
setCurrentUser,
} from "../slices/user/userSlice";
export function* watcherSaga() {
yield takeLatest(logInUser.type, handleLogInUser);
yield takeLatest(getCurrentUser.type, handleGetCurrentUser);
yield takeLatest(setCurrentUser.type, handleSetCurrentUser);
}
the watcher calls handleGetCurrentUser for the saga located in user.tsx file in handler folder:
import { call, put } from "redux-saga/effects";
import { setCurrentUser } from "../../slices/user/userSlice";
import { requestLogInUser, requestGetCurrentUser } from "../requests/user";
export function* handleLogInUser(action) {
try {
console.log(action + "in handleLogInUser");
yield call(requestLogInUser(action));
} catch (error) {
console.log(error);
}
}
export function* handleGetCurrentUser(action) {
try {
const response = yield call(requestGetCurrentUser);
const userData = response;
yield put(setCurrentUser({ ...userData }));
} catch (error) {
console.log(error);
}
}
Which then uses yield call to requestGetCurrentUser which fires off the request to the following user.tsx in requests folder
import axiosInstance from "../../../axios/Axios";
export function requestGetCurrentUser() {
return axiosInstance.request({ method: "get", url: "/user/currentUser/" });
}
The response is given back and put in const userData, I consoleLog()'d the handler and discovered the following:
it will reach the handler successfully
go to the yield call
obtain the data successfully
return the data back to the handler
then it restarts the entire yield call again?
It also never makes it back to the userSlice in order to put the data.
axiosInstance in my axios.tsx file which includes the interceptor and gets the access_token and adds it to the header.
import axios from "axios";
const baseURL = "http://127.0.0.1:8000/api/";
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
Authorization: "Bearer " + localStorage.getItem("access_token"),
"Content-Type": "application/json",
accept: "application/json",
},
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error.config;
if (typeof error.response === "undefined") {
alert(
"A server/network error occurred. " +
"Looks like CORS might be the problem. " +
"Sorry about this - we will get it fixed shortly."
);
return Promise.reject(error);
}
if (
error.response.status === 401 &&
originalRequest.url === baseURL + "token/refresh/"
) {
window.location.href = "/login/";
return Promise.reject(error);
}
if (
error.response.data.code === "token_not_valid" &&
error.response.status === 401 &&
error.response.statusText === "Unauthorized"
) {
const refreshToken = localStorage.getItem("refresh_token");
if (refreshToken) {
const tokenParts = JSON.parse(atob(refreshToken.split(".")[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
if (tokenParts.exp > now) {
return axiosInstance
.post("/token/refresh/", {
refresh: refreshToken,
})
.then((response) => {
localStorage.setItem("access_token", response.data.access);
localStorage.setItem("refresh_token", response.data.refresh);
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
originalRequest.headers["Authorization"] =
"JWT " + response.data.access;
return axiosInstance(originalRequest);
})
.catch((err) => {
console.log(err);
});
} else {
console.log("Refresh token is expired", tokenParts.exp, now);
window.location.href = "/login/";
}
} else {
console.log("Refresh token not available.");
window.location.href = "/login/";
}
}
// specific error handling done elsewhere
return Promise.reject(error);
}
);
export default axiosInstance;
The userSlice.tsx
import { createSlice } from "#reduxjs/toolkit";
const userSlice = createSlice({
name: "user",
initialState: {},
reducers: {
logInUser(state, action) {},
getCurrentUser() {},
setCurrentUser(state, action) {
const userData = action.payload;
console.log(userData + "we are now back in slice");
return { ...state, ...userData };
},
},
});
export const { logInUser, getCurrentUser, setCurrentUser } = userSlice.actions;
export default userSlice.reducer;
I discovered that if I were to remove the authorization token it only fires off once and gets out of the infinite loop since it throws the unauthorised error.
Any suggestions would be greatly appreciated, thanks!
Apologies for getting back so late, I managed to fix it a while ago by pure chance and I dont exactly understand why.
But I believe what fixed it were the following two things:
Changing the useEffect that dispatched the action and ensuring that the handler returned data that the useEffect was expecting to be updated.
In the handler I deconstructed the userData to { userData } which I believe means that the data returned from the axios request is not the entire request but the actual returned data.
my handler
export function* handleGetCurrentUser() {
try {
console.log("in request get user");
const response = yield call(requestGetCurrentUser);
const { data } = response;
yield put(setCurrentUser({ ...data }));
} catch (error) {
console.log(error);
}
}
I forgot to add my useEffect to the post, which created the action.
my useEffect in the App.tsx would dispatch the call when the App was rendered for the first time. However because the returned data did not update what was expected it kept rerendering.
I cant exactly remember what my useEffect was but currently it is the following:
my useEffect in App.tsx
const dispatch = useDispatch();
useEffect(() => {
dispatch(getCurrentUser());
}, [dispatch]);
const user = useSelector((state) => state.user);
As per instructions followed here, I'm trying to cache my endpoint URL and token from Auth0 before constructing my Apollo client:
import React from 'react';
import { ApolloClient, ApolloProvider, from, HttpLink, InMemoryCache } from '#apollo/client';
import { setContext } from '#apollo/link-context';
import { useAuth0 } from './auth/AuthContext';
const App: React.FC = () => {
const { isLoading, getTokenSilently, getIdTokenClaims } = useAuth0();
if (isLoading) return <Loader />;
let endpoint: string;
let token: string;
const contextLink = setContext(async () => {
if (!token) {
token = await getTokenSilently();
}
if (!endpoint) {
endpoint = await getIdTokenClaims()['https://example.com/graphql_endpoint'];
}
return { endpoint, token };
});
/**
* TODO: check for autorization error and remove token from cache
* See: https://www.apollographql.com/docs/react/v3.0-beta/api/link/apollo-link-context/
*/
const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: from([
contextLink,
new HttpLink({
uri: endpoint || '',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
])
});
return (
<ApolloProvider client={apolloClient}>
<div />
</ApolloProvider>
);
};
export default App;
I'm getting the error TS2454 (variable is used before being assigned) for both endpoint and token above. Any idea how I can get around this?
You're declaring both endpoint and token as variables, but not initializing them to anything before checking them inside of setContext.
let endpoint: string;
let token: string;
const contextLink = setContext(async () => {
if (!token) {
token = await getTokenSilently();
}
if (!endpoint) {
endpoint = await getIdTokenClaims()['https://example.com/graphql_endpoint'];
}
return { endpoint, token };
});
Try setting default values:
let endpoint: string = "";
let token: string = "";