我正在使用react-native / apollo-hooks / graphql上的两个令牌(accessToken,refreshToken)进行身份验证流程。并创建了用于updateTokens的阿波罗挂钩。而且我需要在不同的组件中使用此钩子,因为每次用户想要执行任何操作(例如发布帖子或发表评论)时,我都需要调用updateTokens apollo钩子。因此,我需要将updateTokens的所有逻辑带到一个单独的utils中,因为我不想重复自己,并在各处放置相同的代码。但是,当我用apollo挂钩制作单独的util文件时,它不起作用。有人可以解释一下我该怎么做!
import React from 'react'
import { View, Button, Text } from 'react-native'
import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'
const UPDATE_TOKENS = gql`
mutation UpdateTokens($accessToken: String!, $refreshToken: String!) {
updateTokens(data: { accessToken: $accessToken, refreshToken: $refreshToken }) {
user {
name
phone
}
accessToken
refreshToken
}
}
`
const HomeScreen = ({ navigation }) => {
const update_tokens = useMutation(UPDATE_TOKENS)
const updateTokens = (accessToken, refreshToken) => {
console.log('accessToken', accessToken)
console.log('refreshToken', refreshToken)
update_tokens({
variables: { accessToken, refreshToken },
update: async (cache, { data }) => {
const accessToken = data.updateTokens.accessToken
const refreshToken = data.updateTokens.refreshToken
await Keychain.setGenericPassword(accessToken, refreshToken)
}
}).then(() => console.log('We have new credentials'))
}
const getCredentials = async () => {
const tokens = await Keychain.getGenericPassword()
console.log('tokens', tokens)
const keyChainAccessToken = tokens.username
const keyChainRefreshToken = tokens.password
console.log('keyChainAccessToken', keyChainAccessToken)
console.log('keyChainRefreshToken', keyChainRefreshToken)
updateTokens(keyChainAccessToken, keyChainRefreshToken)
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button title="updateTokens" onPress={getCredentials} />
<Button title="getCredentials" onPress={checkCredentials} />
<Button title="SignOut" onPress={userSignOut} />
</View>
)
}
export { HomeScreen }
我可以将其放在这样的单独文件中,然后在不同的地方使用它吗?
import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'
import jwtDecode from 'jwt-decode'
const UPDATE_TOKENS = gql`
mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!) {
updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
user {
name
phone
}
accessToken
refreshToken
}
}
`
const updateCredentials = () => {
const update_tokens = useMutation(UPDATE_TOKENS)
const updateTokens = (refreshToken, refreshTokenId) => {
console.log('refreshToken', refreshToken)
console.log('refreshTokenId', refreshTokenId)
update_tokens({
variables: { refreshToken, refreshTokenId },
update: async (cache, { data }) => {
const newAccessToken = data.updateTokens.accessToken
const newRefreshToken = data.updateTokens.refreshToken
const user = data.updateTokens.user
await Keychain.setGenericPassword(newAccessToken, newRefreshToken)
}
}).then(() => console.log('We have new credentials'))
}
const getCredentials = async () => {
try {
const tokens = await Keychain.getGenericPassword()
console.log('tokens', tokens)
const keychainAccessToken = tokens.username
const keychainRefreshToken = tokens.password
const currentTime = Date.now() / 1000
const decodeAccessToken = jwtDecode(keychainAccessToken)
console.log('decodeAccessToken', decodeAccessToken)
const keychainRefreshTokenId = decodeAccessToken.refreshTokenId
console.log('keychainRefreshTokenId', keychainRefreshTokenId)
if (decodeAccessToken.exp > currentTime) {
console.log('Credentials is still valid')
} else if (decodeAccessToken.exp < currentTime) {
console.log('Go to Update!')
updateTokens(keychainRefreshToken, keychainRefreshTokenId)
}
} catch (err) {
throw new Error('Invalid credentials')
}
}
}
export { updateCredentials }
答案 0 :(得分:0)
对我来说,我不会为了刷新令牌而这样做。我为此做了一个中间件。
如何设置中间件:
import { createUploadLink } from 'apollo-upload-client';
const getClient = ({ userStore: { token, refreshToken: refresh_token }, lang }, dispatch) => {
const locale = lang || 'en';
const uploadLink = createUploadLink({
uri: 'http://localhost:4000'
});
const client = new ApolloClient({
link: ApolloLink.from([
getTokensMiddleware(token, refresh_token, locale, dispatch),
uploadLink
]),
cache: new InMemoryCache()
});
return client;
};
中间件:
const getTokensMiddleware = (token, refresh_token, locale, dispatch) => {
return setContext(async (req, { headers, ...others }) => {
if (!token || !refresh_token) return {};
var decoded = jwtDecode(token);
const isExpired = decoded.exp <= Date.now() / 1000 + 120;
var decodedRefresh = jwtDecode(refresh_token);
const isRefreshJWTExpired = decodedRefresh.exp <= Date.now() / 1000;
if(isRefreshJWTExpired) return {};
if (!isExpired) {
return {
...others,
headers: {
...headers,
Authorization: token ? `Bearer ${token}` : '',
locale
}
};
}
return new Promise((success, fail) => {
refreshToken(refresh_token)
.then(response => {
if (response.ok) {
return response.json();
} else {
fail(response);
}
})
.then(json => {
const { token } = json.data.refreshToken;
dispatch({type: "login", payload: json.data.refreshToken})
success({
...others,
headers: {
...headers,
Authorization: token ? `Bearer ${token}` : '',
locale
}
});
});
});
});
};
您的刷新功能:
const refreshToken = refreshToken => {
const data = {
operation: 'RefreshTokenMutation',
query:
'mutation RefreshTokenMutation($email: String!, $refreshToken: String!) { refreshToken(email: $email, refreshToken: $refreshToken) { token refreshToken user { id email username displayname role { title permissions __typename } __typename } __typename }}',
variables: { email: 'admin_email', refreshToken: refreshToken }
};
return fetch('http://localhost:4000/', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(data)
});
};
使用方法
const AppPage = () => {
const { state, dispatch } = useStore(); //a hook for getting context containing useReducer
return (
<I18nextProvider i18n={i18n}>
<Helmet>
<title>My Project</title>
<meta name="description" content="My project" />
</Helmet>
<ApolloProvider client={getClient(state, dispatch)}>
<LocaleProvider locale={state.antdLocale}>
<App />
</LocaleProvider>
</ApolloProvider>
</I18nextProvider>
);
};
答案 1 :(得分:0)
如果您仍然希望这样做,可以采用HOC方式进行。
import React from 'react';
import YOUR_FUNCTIONS from '~/functions_location';
const withRefreshToken = () => (Component) => {
return <Component {...YOUR_FUNCTIONS} />
};
export {
withRefreshToken
}
之后,您可以这样做:
export default withRefreshToken(HomeScreen);