Skip to content

Instantly share code, notes, and snippets.

@lmcgartland
Last active April 5, 2020 16:03
Show Gist options
  • Save lmcgartland/2a7bc572d4b946c59bc902fe14afd3d1 to your computer and use it in GitHub Desktop.
Save lmcgartland/2a7bc572d4b946c59bc902fe14afd3d1 to your computer and use it in GitHub Desktop.
// Create customFetch function for handling re-authorization
// This customFetch (or any fetch you pass to the link) gets uri and options as arguments. We'll use those when we actually execute a fetch.
const customFetch = (uri, options) => {
// In our application, the refresh token is stored in a redux store
// We create an instance of the state here so we can get the refresh token later in our request
let state = store.getState()
// This reference to the refreshingPromise will let us check later on if we are executing getting the refresh token.
this.refreshingPromise = null;
// Create initial fetch, this is what would normally be executed in the link without the override
var initialRequest = fetch(uri, options)
// The apolloHttpLink expects that whatever fetch function is used, it returns a promise.
// Here we return the initialRequest promise
return initialRequest.then((response) => {
return(response.json())
}).then((json) => {
// We should now have the JSON from the response of initialRequest
// We check that we do and look for errors from the GraphQL server
// If it has the error 'User is not logged in' (that's our implementation of a 401) we execute the next steps in the re-auth flow
if (json && json.errors && json.errors[0] && json.errors[0].message === 'User is not logged in.') {
if (!this.refreshingPromise) {
// Grab the refresh token from the store
var refresh_token = state.account.loginData.refresh_token
// Grab the client_id from our config
var client_id = Config.REACT_APP_CLIENT_ID
// Create the address to grab a new token from
// This endpoint may vary based on your Oauth server
var address = Config.REACT_APP_SERVER_ADDRESS+'/o/token/?grant_type=refresh_token&refresh_token='+refresh_token+'&client_id='+client_id
// Execute the re-authorization request and set the promise returned to this.refreshingPromise
this.refreshingPromise = fetch(address, { method: 'POST' })
.then((refresh_token_repsonse) => {
if(refresh_token_repsonse.ok){
return refresh_token_repsonse.json().then((refreshJSON) => {
// Save the new refresh token to your store or wherever you are keeping it
// saveRefreshToken(refreshJSON.refresh_token)
// Return the new access token as a result of the promise
return refreshJSON.access_token
})
}else{
// If the re-authorization request fails, handle it here
// You can log user out, or display some sort of session has ended alert
// logUserOut()
}
})
}
return this.refreshingPromise.then((newAccessToken) => {
// Now that the refreshing promise has been executed, set it to null
this.refreshingPromise = null;
// Set the authorization header on the original options parameter to the new access token we got
options.headers.authorization = `Bearer ${newAccessToken}`
// Return the promise from the new fetch (which should now have used an active access token)
// If the initialRequest had errors, this fetch that is returned below is the final result.
return fetch(uri, options);
})
}
// If there were no errors in the initialRequest, we need to repackage the promise and return it as the final result.
var result = {}
result.ok = true
result.json = () => new Promise(function(resolve, reject) {
resolve(json);
})
return result
})
}
@gex
Copy link

gex commented Feb 19, 2018

first of all, thanks for your article, it was really helpful! however i run into a problem, all of the props coming from the json response passed to the child component disappeared. i checked the source code of apollo-link-http and it turned out they've changed the way how it's parsing the response and now they're using the .text() method of the response object instead of .json().

@elistone
Copy link

elistone commented Aug 14, 2018

@lmcgartland this does not seem to work any more, could be as @gex has mentioned changes have been made to how they parse the response.

EDIT:

I have been playing with this code and have manged to get it doing what I want, I have created a gist about this here: Apollo VueJS - Token Re-authorisation

Hopefully this will help someone else in the future with this problem.

@grgurev
Copy link

grgurev commented Jun 3, 2019

Any idea why my this is undefined. I don't know if it is because of that or something else but this works perfectly when client is issuing only one request, but if I have several requests, only first is passing and getting token, for the rest I'm getting unauthorised which is expected of course. Any idea how to solve for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment