Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

[Interceptors] Resend failed request after Refresh Token #305

Closed
hfalucas opened this issue Jun 28, 2016 · 7 comments
Closed

[Interceptors] Resend failed request after Refresh Token #305

hfalucas opened this issue Jun 28, 2016 · 7 comments

Comments

@hfalucas
Copy link

// Attach the token on every request
Vue.http.interceptors.push((request, next) => {
    let token = Cookies.get('jwt.token')

    if (token) {
        request.headers = request.headers || {}
        request.headers.Authorization = `Bearer ${token}`
    }

    next()
})

// Attempts to refresh the token
Vue.http.interceptors.push((request, next) => {
   next((response) => {
        if (response.status === 401) {

           return Authentication.refresh().then((result) => {
                Cookies.set('jwt.token', result.data.token)
                request.headers.Authorization = `Bearer ${result.data.token}`

                return Vue.http(request).then((data) => {
                    return data
                })

            }).catch(() => {
                Authentication.logout()
                return router.go({name: 'login'})
            })
        }
    })
})

Everything is working fine, the token is being attached on every request and it can be refreshed.

The problem is when it tries to make the failed request(s) after it gets the new token, the request is indeed made but the response is not "passed to the vm?" so it gets always in the loading state. If I manually refresh the page it works fine.

Is there something that I am missing or is my logic/flow wrong?

PS: I think an example of a generic refresh token flow would be a good addition to the code recipes section.

@hfalucas hfalucas changed the title New Interceptors [Interceptors] Resend failed request after Refresh Token Jun 28, 2016
@steffans
Copy link
Member

steffans commented Jul 1, 2016

I added support for Promise return values in a response interceptors with v0.9.2. Before this version the return value of a response interceptor was not evaluated.

@hfalucas
Copy link
Author

hfalucas commented Jul 5, 2016

Sorry for the late reply @steffans. It is in fact working now :) I though it was something wrong with my logic.
You are best. Thank you 👍

@hfalucas hfalucas closed this as completed Jul 5, 2016
@williamokano
Copy link

@hfalucas Hi Lucas, I was looking for your example of refresh token and re-send.

Can you please send me the code for Authentication.refresh()?

Or just answer: is this refresh method returning a promise?

Thanks in advance.

@hfalucas
Copy link
Author

@williamokano Yes the Authentication.refresh() is returning a promise :)

export default {
    // (..)
    refresh () {
        return Vue.http.get(`${API}/v1/refresh-token`)
    }
}

@cve
Copy link

cve commented Mar 22, 2017

I have infinite loop of 401 responses and refresh token requests with there interceptors:

Vue.http.interceptors.push((request, next) => {
  const token = auth.getToken()

  if (token) {
    request.headers.set('Content-Type', 'application/json')
    request.headers.set('Authorization', `Bearer ${token}`)
  }

  next()
})
Vue.http.interceptors.push((request, next) => {
  next((response) => {
    if (response.status === 401) {
      return auth.refresh().then(result => {
        auth.setToken(result.data.token)
        auth.setRefreshToken(result.data.refresh_token)
        request.headers.set('Authorization', `Bearer ${result.data.token}`)

        return Vue.http(request).then(data => {
          return data
        })
      }).catch(() => {
        auth.logout()
        return router.push('/login')
      })
    }
  })
})

What am I doing wrong?

@james2doyle
Copy link

@cve I put a guard like so, seemed to work well for me:

// outside state that lets us know if we need to refresh or not
let allowRefresh = true;

Vue.http.interceptors.push((request, next) => {
  next((response) => {
    // are we allowed to refresh AND is this a 401?
    if (allowRefresh && response.status === 401) {
      // we can only allow them to refresh once,
      // so turn this toggle off so they will be redirected if
      // the refresh response is also a 401
      allowRefresh = false;
      return auth.refresh().then(result => {
        auth.setToken(result.data.token)
        auth.setRefreshToken(result.data.refresh_token)
        request.headers.set('Authorization', `Bearer ${result.data.token}`)

        return Vue.http(request).then(data => {
          return data
        })
      }).catch(() => {
        auth.logout()
        return router.push('/login')
      })
    }
  })
})

@vanslly
Copy link

vanslly commented Oct 8, 2017

@james2doyle That will log the user out when the original request returns an error status code like HTTP 500, instead use onrejected callback instead.

So...

// outside state that lets us know if we need to refresh or not
let allowRefresh = true;

Vue.http.interceptors.push((request, next) => {
  next((response) => {
    // are we allowed to refresh AND is this a 401?
    if (allowRefresh && response.status === 401) {
      // we can only allow them to refresh once,
      // so turn this toggle off so they will be redirected if
      // the refresh response is also a 401
      allowRefresh = false;
      return auth.refresh().then(result => {
        auth.setToken(result.data.token)
        auth.setRefreshToken(result.data.refresh_token)
        request.headers.set('Authorization', `Bearer ${result.data.token}`)

        return Vue.http(request).then(data => {
          return data
        })
      }, () => {
        auth.logout()
        return router.push('/login')
      })
    }
  })
})

That way the original error handler for the original request will handle the error for the original request.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants