How to add token in nuxt axios instanse - vue.js

I try to create axios instance and add token header.
const apiClient = axios.create({
baseURL: "..."
});
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem("auth._token.local");
if (token) {
config.headers["Authorization"] = 'Bearer ' + token;
}
return config;
},
function(error) {
return Promise.reject(error);
}
);
But it throw an error localStorage is not defined.
I think the error throw because I use it in fetch hook on page, but there is no localStorage on server-side. How should I modify my code?

If this piece of code is indeed inside fetch, as you say, you are correct. fetch is called first on the server, so there is no localStorage because you have no localStorage API without a browser.
You could use nuxt's context to check whether you are on the client or the server like:
// Client-side
if (process.client) {
// ...your code checking for localhost
} else {
// do whatever fallback on the server side....
}
more info:
https://nuxtjs.org/api/context

Related

How do I pull data out of response or cache for apollo client 3

I am trying to pull a jwt out of a loginUser mutation and store it in a variable then use the apollo-link to setContext of the header via "Authorization: Bearer ${token} for authentification as all my other mutations and queries require the token. I have been slamming the docs for days on Apollo Client(React) -v 3.3.20. I have been through all the docs and they show all these examples of client.readQuery & writeQuery which frankly seem to just refetch data? I don't understand how you actually pull the data out of the response and store it in a variable.
The response is being stored in the cache and I have no idea how to take that data and store it in a token variable as I stated above. Which remote queries I can just access the returned data via the data object from the useQuery hook, however on the useMutation hook data returns undefined. The only thing I could find on this on stack overflow was the my data type may be custom or non-traditional type but not sure if that is the problem.
[Cache in apollo dev tools][1]
[Mutation in apollo dev tools][2]
[Response in network tab][3]
Here is my ApolloClient config:
const httpLink = createHttpLink({ uri: 'http://localhost:4000/',
// credentials: 'same-origin'
});
const authMiddleware = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('token');
// add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: `Bearer ${token}` || null,
}
}));
return forward(operation);
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: concat(authMiddleware, httpLink),
});
The header works obviously I just can't grab the token to pass so the header just sends Authorization: Bearer.
For the login I have this:
const LOGIN_USER = gql`
mutation($data:LoginUserInput!) {
loginUser(
data: $data
) {
user {
id
name
}
token
}
}
`;
const [loginUser, { data, loading, error }] = useMutation(LOGIN_USER);
if (loading) return 'Submitting...';
if (error) return `Submission error! ${error.message}`;
Originally I was just calling
onClick={loginUser( { variables })}
For the login but onComplete never works and everywhere I look I see lots of posts about it with no solutions. So I tried slamming everything into a function that I then called with loginUser inside it:
const submit = async () => {
loginUser({ variables})
// const { user } = await client.readQuery({
// query: ACCESS_TOKEN,
// })
// console.log(`User : ${JSON.stringify(user)}`)
const token = 'token';
const userId = 'userId';
// console.log(user);
// localStorage.setItem(token, 'helpme');
// console.log({data});
}
At this point I was just spending hours upon hours just trying mindless stuff to potentially get some clue on where to go.
But seriously, what does that { data } in useMutation even do if it's undefined. Works perfectly fine for me to call data.foo from useQuery but useMutation it is undefined.
Any help is greatly appreciated.
[1]: https://i.stack.imgur.com/bGcYj.png
[2]: https://i.stack.imgur.com/DlzJ1.png
[3]: https://i.stack.imgur.com/D0hb3.png

Vue axios doesnt change header

I am quite new to vue and I am trying to send a request to my api using axios.
I build an interceptor which seems to work (logging is happening)
export default function setup() {
console.log('Http interceptor starting...')
Axios.interceptors.request.use((request) => {
const token = store.getters.token;
if (token) {
request.headers.Authorization = `Bearer ${token}`;
}
console.log(request);
return request
}, (err) => {
return Promise.reject(err);
});
}
If I check the console I can see the request including the token. If I check my network tab in the browser i can see the same request without the token. If I check the console of my api the token is null. Any Ideas?
Edit: If I use postman with the same request and the same token it is working as it shuld

Nuxtjs Axios onRequest() not executed for asnycData request

I have configured axios plugin onRequest helper to set Authorization header on API requests like below
1. export default function({ $axios, redirect, app, store }) {
2. $axios.onRequest(config => {
3. var requestURL = config.url;
4. console.log("Making request to " + requestURL);
5. const token = store.state.user.account.token;
6. if (token) {
7. config.headers.common["Authorization"] = `Bearer ${token}`;
8. console.log("Added authorization header");
9. }
10. });
This onRequest helper get invoked for actions in store. But for asyncData in page component, call reaches till line 2 above but never enters the onRequest function.
import axios from "axios";
asyncData({ params }) {
console.log("params: " + params);
return axios
.get(`http://localhost:8080/user/id/${params.id}`)
.then(res => {
return { userData: res.data };
})
.catch(e => {
error({ statusCode: 404, message: "User not found" });
});
}
As I understand all Axios requests should pass through onRequest helper function. Am I missing something here? How to fix this?
When you import axios from 'axios' you're not using the configuration for the axios instance that you specified initially. Instead, you're using an entirely new axios instance.
The correct way would be to leverage the $axios instance from the Nuxt context.
asyncData(context) {
await context.$axios...
}
You can also use the $axios instance directly. An example is:
asyncData({$axios}) {
await $axios...
}

Automatically log out user when token is invalidated

I have a SPA that is built on vuejs. When a user is logged in via API, the token is stored in local storage.
I need a global solution which will logout and prompt the user when the token is no longer valid. At the moment, I get "invalid token" error when accessing private API endpoints.
How do I rig axios so that ALL response of invalid tokens will trigger the logout/prompt code?
Here is an simple example with axios. It use a Bearer token for authentification.
import axios from "axios";
import { useUserStore } from "#/store/userStore";
const apiClient = axios.create({
baseURL: ""http://127.0.0.1:8001",
headers: {},
});
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const config = error?.config;
if (error?.response?.status === 401) {
const result = await refreshToken();
if (result) {
config.headers = {
...config.headers,
authorization: `Bearer ${result?.token}`,
};
}
return axios(config);
}
);
const refreshToken = async () => {
/* do stuff for refresh token */
// if refresh token failed
try {
useUserStore().actionLogout();
} catch (error) {
console.log(error);
} finally {
loacalStorage.clear();
}
};
you can write a function that clears your local storage after some time and logout user

vue-resource not passing token in request headers

I'm new to Vuejs 2, currently using vue-resource to retrieve data from the server. However, I would need a token passed in the request header at the same time in order to retrieve the data from the server.
So the problem is, I am unable to retrieve data because the token is not passed into the request header, using vue-resource.
Here is the method that uses the vue-resource's interceptor (to pass in the token) to intercept the GET request:
test () {
this.$http.interceptors.push((request) => {
var accessToken = window.localStorage.getItem('access_token')
request.headers.set('x-access-token', accessToken)
return request
})
this.$http.get(staffUrl)
.then(response => {
console.log(response)
}, (response) => {
console.log(response)
})
}
Documentation for vue-resource, HTTP: https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
When I try to GET the data, i end up with an error 403 (forbidden) and after checking the request headers in the dev tools, I also could not find the token in the request headers.
Please tell me where I went wrong because I'm really new to this so i appreciate any help! Thank you!
Setting interceptors inside the component using $http doesn't work, or at least it doesn't in my testing. If you examine/log this.$http.interceptors right after your push in the test method, you'll note that the interceptor was not added.
If you add the interceptor before you instantiate your Vue, however, the interceptor is added properly and the header will be added to the request.
Vue.http.interceptors.push((request, next) => {
var accessToken = "xyxyxyx"
request.headers.set('x-access-token', accessToken)
next()
})
new Vue({...})
Here is the test code I was using.
Also note, if you are using a version prior to 1.4, you should always call the next method that is passed to the interceptor. This does not appear to be necessary post version 1.4.
please go through this code
import vueResource from "vue-resource";
import { LocalStorage } from 'quasar'
export default ({
app,
router,
Vue
}) => {
Vue.use(vueResource);
const apiHost = "http://192.168.4.205:8091/";
Vue.http.options.root = apiHost;
Vue.http.headers.common["content-type"] = "application/json";
Vue.http.headers.common["Authorization"] = "Bearer " + LocalStorage.get.item("a_t");
Vue.http.interceptors.push(function(request, next) {
console.log("interceptors", request);
next(function(response) {
});
});
}