Created
June 13, 2018 11:09
-
-
Save lindesvard/8bd0aa751a4c4e84072000222232f648 to your computer and use it in GitHub Desktop.
ApolloClient with JWT auth and refetch jwt token with refreshtoken
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
import config from 'config' | |
import { getAccessToken } from 'libs/storage' | |
import { ApolloClient } from 'apollo-client' | |
import { createHttpLink } from 'apollo-link-http' | |
import { setContext } from 'apollo-link-context' | |
import { InMemoryCache } from 'apollo-cache-inmemory' | |
import customFetch from './customFetch' | |
const httpLink = createHttpLink({ | |
uri: config.GRAPHQL_URL, | |
fetch: customFetch, | |
}) | |
const getAuthHeaders = async (currentToken = null) => { | |
const token = currentToken || (await getAccessToken()) | |
return token ? { authorization: `Bearer ${token}` } : {} | |
} | |
const authLink = setContext(async (_, { headers }) => ({ | |
headers: { | |
...headers, | |
...(await getAuthHeaders()), | |
}, | |
})) | |
export default new ApolloClient({ | |
link: authLink.concat(httpLink), | |
cache: new InMemoryCache(), | |
}) |
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
import { get } from 'lodash' | |
import { getRefreshToken, setAccessToken } from './storage' | |
import config from 'config' | |
export default async (uri, options) => { | |
this.tokenRequest = null | |
const initialRequest = await fetch(uri, options) | |
const clonedRequest = await initialRequest.clone() | |
const response = await clonedRequest.json() | |
const accessTokenHasExpired = | |
get(response, 'errors', []).filter(error => error.message === 'jwtExpired') | |
.length > 0 | |
if (accessTokenHasExpired && !this.tokenRequest) { | |
console.log('accessToken has expired, try getting a new one') | |
this.tokenRequest = await fetch(config.API_URL + '/auth/token', { | |
method: 'POST', | |
headers: { | |
Accept: 'application/json', | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
refreshToken: await getRefreshToken(), | |
}), | |
}) | |
if (this.tokenRequest.status !== 200) { | |
// TODO: clear everything about the user | |
// and send them to home screen | |
console.log('Could not update the accessToken #1') | |
return initialRequest | |
} | |
const { accessToken } = await this.tokenRequest.json() | |
options.headers.authorization = `Bearer ${accessToken}` | |
console.log('Fetch with new accessToken', accessToken) | |
this.tokenRequest = null | |
setAccessToken(accessToken) | |
return fetch(uri, options).catch(res => { | |
console.log('Could not update the accessToken #2') | |
}) | |
} | |
return initialRequest | |
} |
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
import { AsyncStorage } from 'react-native' | |
const ACCESS_TOKEN = 'accessToken' | |
const REFRESH_TOKEN = 'refreshToken' | |
// for some caching | |
let accessToken = null | |
let refreshToken = null | |
export const setAccessToken = token => { | |
accessToken = token | |
return AsyncStorage.setItem(ACCESS_TOKEN, token) | |
} | |
export const setRefreshToken = token => { | |
refreshToken = token | |
return AsyncStorage.setItem(REFRESH_TOKEN, token) | |
} | |
export const getAccessToken = async () => { | |
if (accessToken) return accessToken | |
accessToken = await AsyncStorage.getItem(ACCESS_TOKEN) | |
return accessToken | |
} | |
export const getRefreshToken = async () => { | |
if (refreshToken) return refreshToken | |
refreshToken = await AsyncStorage.getItem(REFRESH_TOKEN) | |
return refreshToken | |
} | |
export const clearTokens = () => { | |
accessToken = null | |
refreshToken = null | |
return Promise.all([ | |
AsyncStorage.removeItem(ACCESS_TOKEN), | |
AsyncStorage.removeItem(REFRESH_TOKEN), | |
]) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment