-
-
Save alfonmga/9602085094651c03cd2e270da9b2e3f7 to your computer and use it in GitHub Desktop.
import { onError } from 'apollo-link-error'; | |
import { Observable } from 'apollo-link'; | |
import { buildAuthHeader } from 'utils/requests'; | |
import { getProvider as getGlobalProvider } from 'GlobalState'; | |
let isFetchingToken = false; | |
let tokenSubscribers = []; | |
function subscribeTokenRefresh(cb) { | |
tokenSubscribers.push(cb); | |
} | |
function onTokenRefreshed(err) { | |
tokenSubscribers.map(cb => cb(err)); | |
} | |
/* eslint-disable consistent-return */ | |
const refreshAuthTokenLink = () => | |
onError( | |
({ graphQLErrors, networkError, operation, response, forward }) => | |
new Observable(async observer => { | |
if (graphQLErrors) { | |
graphQLErrors.map(async ({ extensions }, index) => { | |
switch (extensions.code) { | |
case 'UNAUTHENTICATED': { | |
const retryRequest = () => { | |
operation.setContext({ | |
headers: { | |
...headers, | |
Authorization: buildAuthHeader( | |
globalProvider.authAccessToken(), | |
).Authorization, | |
}, | |
}); | |
const subscriber = { | |
next: observer.next.bind(observer), | |
error: observer.error.bind(observer), | |
complete: observer.complete.bind(observer), | |
}; | |
return forward(operation).subscribe(subscriber); | |
}; | |
const { headers } = operation.getContext(); | |
const globalProvider = await getGlobalProvider(); | |
if (!isFetchingToken) { | |
isFetchingToken = true; | |
try { | |
await globalProvider.refreshAccessTokenReq(); | |
isFetchingToken = false; | |
onTokenRefreshed(null); | |
tokenSubscribers = []; | |
return retryRequest(); | |
} catch (e) { | |
onTokenRefreshed( | |
new Error('Unable to refresh access token'), | |
); | |
tokenSubscribers = []; | |
isFetchingToken = false; | |
await globalProvider.logOut({ isForced: true }); | |
return observer.error(graphQLErrors[index]); | |
} | |
} | |
const tokenSubscriber = new Promise(resolve => { | |
subscribeTokenRefresh(errRefreshing => { | |
if (!errRefreshing) return resolve(retryRequest()); | |
}); | |
}); | |
return tokenSubscriber; | |
} | |
default: | |
return observer.next(response); | |
} | |
}); | |
} | |
if (networkError) { | |
return observer.error(networkError); | |
} | |
}), | |
); | |
export default refreshAuthTokenLink; |
@d4rky-pl oh my bad, thank you. I updated it.
Hey please can you explain what is, or maybe to include those files/functions too: buildAuthHeader
, getProvider
.
I presume that utils/requests
is fetch to auth server to get new access token or?? But I really can't tell what is globalProvider ..
Hey please can you explain what is, or maybe to include those files/functions too:
buildAuthHeader
,getProvider
.
I presume thatutils/requests
is fetch to auth server to get new access token or?? But I really can't tell what is globalProvider ..
buildAuthHeader
is just a simple function I use to build the authorization header: https://github.com/AMGAVentures/saas-boilerplate/blob/develop/packages/app/src/utils/requests.js
getProvider
is where I keep my global application state (user authentication/profile state..etc) check out more about it here: https://github.com/AMGAVentures/saas-boilerplate/blob/develop/packages/app/src/GlobalState.js.
You don't need them to make it works.. you can refactor/tweak those part as you need.
I've been struggling to get my refresh token logic to work for multiple failed request for a couple days now, and this finally solved it for me. Thank you for sharing this!
I tripped over this so badly my face still hurts: If you want to skip the operation, don't do
return null
but runobserver.error(networkError)
instead. Otherwise it'll get stuck and theclient.query
Promise will never get resolved.