In the main.js file i have set the vue resource to use auth headers with every requests:
Vue.use( VueResource )
let auth = validToken()
if( auth ) {
Vue.http.interceptors.push( ( request, next ) => {
request.headers.set( 'Authorization', auth.token )
request.headers.set( 'Accept', 'application/json' )
next()
} )
}
And in the logout, i am trying to delete the header this way,
resetVueRsr: () => {
this.$http.headers.common['Authorization'] = null
}
And when a user log in, then resetting the new token to the global Vue resource like this,
this.$http.headers.common['Authorization'] = res.body.token
But once a new user is logging in the site, the vue resource still sending previous token with each request. What i am missing here?
I checked the source, and it looks like all defined interceptors are added in some internal array, and then used for requests.
According to your code sample, you obtain an object with initial token somehow and then pass it inside your arrow function, so JS creates a closure for this to make this object available inside the function, and I think a lifetime of this closure is equal to a lifetime of your app. I assume that you have a sort of SPA.
Also from the source it looks like interceptors are not related to common headers, so your way of deleting and resetting won't change your interceptor.
Related
I'm pretty new in React-Native programming, but here is the context.
We are using React Query and Axios libraries in our project. As AuthManager we are using Keycloak and for the library managing auth status we have React Native Keycloak. We encounter a tedious problem with our server responding randomly 401 at our requests after a certain amount of time, bringing also to the app crash sometimes.
We reproduced the error making the Bearer Token of Keycloak expire after only 1 minute. This caused almost immediatly the 401 error and we wondered why this is happening.
Let's say we have a screen with some "Activities" and this screen is the first thing the user will see. For handling requests, in our code we use some custom hooks that reference useQuery, for example:
export function useActivities(): UseQueryResult<ActivityList> {
const { headers } = useHeaders();
return useQuery(
['activities', today.start],
() => getActivitiesList(headers), // Note 1
{
enabled: !!today.start,
}
);
}
The important point of it is that we useHeaders to get our updated headers with the Keycloak token and our realm settings. useHeaders is almost everywhere in our app.
export function useHeaders(): UseHeaders {
const { keycloak } = useKeycloak();
const KEYCLOAK_REALM = remoteConfig().getString('KEYCLOAK_REALM');
const headers = {
Authorization: `Bearer ${keycloak?.token}`,
Realm: KEYCLOAK_REALM,
};
return { headers };
}
Now, the getActivitiesList is simple as five:
async function getActivitiesList(headers: UseHeaders['headers']): Promise<ActivityList> {
const url = `${BASE_URL}${VERSION}/activities/grouped?end=${end}&start=${start}`;
// Note 2
return axios
.get(url, { headers })
.then((res) => res.data)
.catch((e) => console.error('Error fetching grouped activities:', e));
}
The problem with all of that is that whenever Keycloak will trigger the refresh token, the token inside keycloak object is changed, the headers inside useActivities are changed BUT if I print the headers inside my getActivitiesList (// Note 2), or even inside the query function (// Note 1), headers will not be updated. Sometimes it just causes to make two requests (one that fails and show error, the other one actually working), some other times the app crashes without any explain. This makes me wonder why the query function will not update its headers and passed the "old" headers inside getActivitiesList.
For now we are mitigating this problem in two different points.
After keycloak init, we pass immediatly to axios a global header with axios.defaults.headers.common.Realm = KEYCLOAK_REALM;
After receiving a valid token from Keycloak, we overwrite the Authorization header with a new one: axios.defaults.headers.common.Authorization = 'Bearer ${keycloak?.token}';
This is not a perfect solution and we are here to search some info about this problem.
Someone get something similar? How to manage the token refresh in useQuery function?
I'm wondering if it's possible to essentially "reevaluate" the middleware conditions without actually changing the current route.
The middleware's purpose is to prevent non-logged-in users from accessing the "dashboard".
My issue is, a user could become logged in or logged out without necessarily changing route but they wouldn't be redirected until they try and change pages.
I have a VueX action that triggers when the user's auth state changes but this (from what I can see), can't access the redirect or route variables.
// /mixins/auth.js
const reevaluateAuthStatus = (store, redirect, route) => {
console.log(route)
const redirectPolicy = route.meta.map((meta) => {
if (meta.auth && typeof meta.auth.redirectPolicy !== 'undefined') { return meta.auth.redirectPolicy[0] }
return []
})
const user = store.getters['auth/getUser']
if (redirectPolicy.includes('LOGGEDOUT')) {
if (user) {
return redirect('/dashboard')
}
} else if (redirectPolicy.includes('LOGGEDIN')) {
if (!user) {
return redirect('/login')
}
}
}
module.exports = {
reevaluateAuthStatus
}
// /middleware/auth.js
import { reevaluateAuthStatus } from '../mixins/auth'
export default function ({ store, redirect, route }) {
reevaluateAuthStatus(store, redirect, route)
}
Appreciate any help on this :)
You cannot re-evaluate a middleware AFAIK because it's mainly this (as stated in the documentation)
middlewares will be called [...] on the client-side when navigating to further routes
2 clean ways you can still achieve this IMO:
use some websockets, either with socket.io or something similar like Apollo Subscriptions, to have your UI taking into account the new changes
export your middleware logic to some kind of call, that you could trigger again by calling the $fetch hook again or any other data-related fetching hook in Nuxt
Some more ugly solutions would probably be:
making an internal setInterval and check if the actual state is still valid every 5s or so
move to the same page you are actually on with something like this.$router.go(0) as somehow explained in the Vue router documentation
Still, most of the cases I don't think that this one may be a big issue if the user is logged out, because he will just be redirected once he tries something.
As if the user becomes logged-in, I'm not even sure on which case this one can happen if he is not doing something pro-active on your SPA.
I don't know if it's relevant or not, but I solved a similar problem this way:
I have a global middleware to check the auth status. It's a function that receives Context as a parameter.
I have a plugin that injects itself into context (e.g. $middleware).
The middleware function is imported here.
In this plugin I define a method that calls this middleware passing the context (since the Plugin has Context as parameter as well): ctx.$middleware.triggerMiddleware = () => middleware(ctx);
Now the middleware triggers on every route change as intended, but I can also call this.$middleware.triggerMiddleware() everywhere I want.
My API KEY = 2356yhtujkiw
I am using AXIOS on VUEJS as for get/post request.
API document says to add that API key as on header of all requests.
I tried setting it as axios.defaults.headers.common['header'] = '2356yhtujkiw'; but it did not work.
What's the proper way to define API KEY on header ?
it can get confusing when you're starting out, and certain terms, like header could mean different things depending on context.
There are several ways you can achieve axios calls in vue.
There's the "easy" one, where you add stuff right into a component
you can integrate it into vuex
create a custom helper/wrapper function (to use in a component or vuex)
use a the vue axios plugin
On top of that there's more than one ways to implement axios
reuse global instance
create new instance for every call
without seeing your code it's hard to know which way you're using it, but I'm going to try to give steps for a way that should make it easy enough to replicate
create a api.js in your src folder with:
import axios from 'axios'
let TOKEN = null;
export default {
setHeader(val){
TOKEN = val;
},
fetchUsers: () => {
const instance = axios.create({
baseURL: 'https://api.example.com',
headers: {
header: TOKEN
}
});
// or: instance.defaults.headers.common['header'] = TOKEN;
return instance.get('/users')
.then((result) => {
this.users = result.data
})
}
}
In a component, or Vuex, you can then...
import api from '../api.js'
// set header
api.setHeader('abc123')
// make api call
api.fetchUsers().then(r=> {console.log(r)});
This (though untested code) should work...
It's not the cleanest way of using it, but should be easy to implement in existing code.
TL;DR;
The reason axios.defaults.headers.common['header'] = '2356yhtujkiw'; doesn't work is likely because you've already created the instance, and are re-using it. Updating the default would only apply for subsequent instances created. The example above gets around that, by not using any defaults, and just inserting the headers in every new instance, which is created for every new call.
I have added the hcaptcha widget to my login component using this package: https://github.com/hCaptcha/vue-hcaptcha. The challenge works as expected on the front end.
The response object as viewed in the network tab includes a token and looks like this:
expiration: 120
generated_pass_UUID: "P0_eyJ0eXAiOiJKV1QiLCJhbG...O9U"
pass: true
My question is how to pass that token with my email and password when I submit the login form.
Normally, I am using axios to make an explicit api call and I can define a variable like:
let response = axios.get('/whater_api')
and then use response.data to access whatever comes back. But I can't see how to do that here.
Have you tried the #verify="onVerify" event? it seems the result is emitted on that event, try to add methods onVerify on your vue instance like below:
methods: {
onVerify: function(e) {
console.log(e);
}
}
if it does return the response, you can make an object for the token, your email and password and the rest is just as usual.
I'm using Axios in my VueJS application and I want to add a default GET param in my request. I send my API-KEY through the URL ?api-key=secret and I don't want to specify this parameter each time.
I see in the documentation that we can set Global custom defaults. With that we don't have to specify the header each time. But can we do the same for get param ?
I struggled to get this to work with axios instances using the two most commonly suggested methods:
// method 1: setting axios.defaults.params at the class level
axios.defaults.params = {}
axios.defaults.params['api-key'] = secret
and
// method 2: setting the `params` attribute at an instance level
const axClient = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
params: {
api-key: process.env.VUE_APP_API_KEY
}
});
I did however, manage to get it working nicely using interceptors like this:
// create an instance with default properties
const axClient = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
});
axClient.interceptors.request.use((config) => {
// use config.params if it has been set
config.params = config.params || {};
// add any client instance specific params to config
config.params['api-key'] = process.env.VUE_APP_API_KEY;
return config;
});
The benefit of this approach is that the instance level params can be dynamic/computed per request if needed.
As a (slightly contrived) example, if you needed to add a JWT to each request (including any logic to fetch it from your storage method of choice) and even react to the logic around that. So in this toy example, if the user doesn't have a JWT in storage, redirect them to the login page instead of making the request.
Here it is:
axios.defaults.params = {}
axios.defaults.params['api-key'] = secret
If you need to call a function before each axios request, you should use an interceptor.