-
-
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/requestsis 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 nullbut runobserver.error(networkError)instead. Otherwise it'll get stuck and theclient.queryPromise will never get resolved.