Created
March 21, 2018 16:11
-
-
Save MrLoh/1ae9e48ceb595207ecb3cfdb9849c083 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import { ApolloClient } from 'apollo-client'; | |
import { ApolloLink } from 'apollo-link'; | |
import { HttpLink } from 'apollo-link-http'; | |
import { RetryLink } from 'apollo-link-retry'; | |
import { AuthLink } from './link-auth'; | |
import cache from './cache'; | |
const retryLink = new RetryLink(); | |
const authLink = new AuthLink(); | |
const httpLink = new HttpLink({ uri: CINURU_API }); | |
const link = ApolloLink.from([reduxLoggerLink, retryLink, authLink, httpLink]); | |
// construct client | |
const client = new ApolloClient({ link, cache }); | |
// inject client dependencies | |
authLink.injectClient(client); | |
export default client; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import { ApolloLink, Observable } from 'apollo-link'; | |
import type { ApolloClient } from 'apollo-client'; | |
import type { Operation, NextLink } from 'apollo-link'; | |
import { getToken, refreshToken } from '../services/auth'; | |
export class AuthLink extends ApolloLink { | |
tokenRefreshingPromise: Promise<boolean> | null; | |
injectClient = (client: ApolloClient): void => { | |
this.client = client; | |
}; | |
refreshToken = (): Promise<boolean> => { | |
if (!this.tokenRefreshingPromise) this.tokenRefreshingPromise = refreshToken(this.client); | |
return this.tokenRefreshingPromise; | |
}; | |
setTokenHeader = (operation: Operation): void => { | |
const token = getToken(); | |
if (token) operation.setContext({ headers: { authorization: `Bearer ${token}` } }); | |
}; | |
request(operation: Operation, forward: NextLink) { | |
// set token in header | |
this.setTokenHeader(operation); | |
// try refreshing token once if it has expired | |
return new Observable(observer => { | |
let subscription, innerSubscription; | |
try { | |
subscription = forward(operation).subscribe({ | |
next: observer.next.bind(observer), | |
complete: observer.complete.bind(observer), | |
error: netowrkError => { | |
if (netowrkError.statusCode === 401) { | |
this.refreshToken().then(success => { | |
if (success) { | |
// set new token and retry operation | |
this.setTokenHeader(operation); | |
innerSubscription = forward(operation).subscribe(observer); | |
} else { | |
// throw error | |
observer.error(new Error('jwt refresh failed')); | |
} | |
}); | |
} else { | |
observer.error(netowrkError); | |
} | |
}, | |
}); | |
} catch (e) { | |
observer.error(e); | |
} | |
return () => { | |
if (subscription) subscription.unsubscribe(); | |
if (innerSubscription) innerSubscription.unsubscribe(); | |
}; | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi everyone,
We solved this problem using PubSub. After the first login, the client subscribes to the refresh_token event. On the backend, we check the token's expiration time with every request. If the token is set to expire within 1 hour, we publish a new token to the client. During the login process, we set a unique event name in Redis for each client, and the client subscribes to this unique event name. When the conditions for refreshing the token are met, we publish the new token to the specified event name, and the client catches the publication and updates the local storage.
This is a solution we came up with on our own, and we are open to any ideas and feedback you may have. Thank you.