use*_*462 15 apollo graphql react-native apollo-client
所以我们正在使用 Apollo 和 GraphQL 创建一个 React-Native 应用程序。我正在使用基于 JWT 的身份验证(当用户同时登录activeToken和refreshToken 时),并希望实现一个流程,当服务器注意到它已过期时,令牌会自动刷新。
Apollo-Link-Error 的 Apollo Docs 提供了一个很好的起点来捕获来自 ApolloClient 的错误:
onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.extensions.code) {
case 'UNAUTHENTICATED':
// error code is set to UNAUTHENTICATED
// when AuthenticationError thrown in resolver
// modify the operation context with a new token
const oldHeaders = operation.getContext().headers;
operation.setContext({
headers: {
...oldHeaders,
authorization: getNewToken(),
},
});
// retry the request, returning the new observable
return forward(operation);
}
}
}
})
Run Code Online (Sandbox Code Playgroud)
但是,我真的很难弄清楚如何实现getNewToken()。我的 GraphQL 端点具有创建新令牌的解析器,但我不能从 Apollo-Link-Error 调用它,对吗?
那么,如果令牌是在 Apollo 客户端将连接到的 GraphQL 端点中创建的,那么如何刷新令牌?
Léo*_*gli 18
Apollo Error Link 文档中给出的示例是一个很好的起点,但假设getNewToken()
操作是同步的。
在您的情况下,您必须点击 GraphQL 端点以检索新的访问令牌。这是一个异步操作,您必须使用apollo-link包中的fromPromise
实用程序函数将您的 Promise 转换为 Observable。
import React from "react";
import { AppRegistry } from 'react-native';
import { onError } from "apollo-link-error";
import { fromPromise, ApolloLink } from "apollo-link";
import { ApolloClient } from "apollo-client";
let apolloClient;
const getNewToken = () => {
return apolloClient.query({ query: GET_TOKEN_QUERY }).then((response) => {
// extract your accessToken from your response data and return it
const { accessToken } = response.data;
return accessToken;
});
};
const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.extensions.code) {
case "UNAUTHENTICATED":
return fromPromise(
getNewToken().catch((error) => {
// Handle token refresh errors e.g clear stored tokens, redirect to login
return;
})
)
.filter((value) => Boolean(value))
.flatMap((accessToken) => {
const oldHeaders = operation.getContext().headers;
// modify the operation context with a new token
operation.setContext({
headers: {
...oldHeaders,
authorization: `Bearer ${accessToken}`,
},
});
// retry the request, returning the new observable
return forward(operation);
});
}
}
}
}
);
apolloClient = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, httpLink]),
});
const App = () => (
<ApolloProvider client={apolloClient}>
<MyRootComponent />
</ApolloProvider>
);
AppRegistry.registerComponent('MyApplication', () => App);
Run Code Online (Sandbox Code Playgroud)
您可以停留在上述运行正常的实现上,直到两个或多个请求同时失败。因此,要在令牌到期时处理并发请求失败,请查看这篇文章。
BIl*_*igt 13
更新 - 2022 年 1 月, 您可以从以下位置查看基本的 React JWT 身份验证设置:https://github.com/bilguun-zorigt/React-GraphQL-JWT-Authentication-Example
我还在存储库的自述文件部分添加了在前端和后端设置身份验证时要考虑的安全点。(XSS攻击、csrf攻击等...)
原始答案 - 2021 年 12 月
我的解决方案:
import { setContext } from '@apollo/client/link/context';
async function getRefreshedAccessTokenPromise() {
try {
const { data } = await apolloClientAuth.mutate({ mutation: REFRESH })
// maybe dispatch result to redux or something
return data.refreshToken.token
} catch (error) {
// logout, show alert or something
return error
}
}
let pendingAccessTokenPromise = null
export function getAccessTokenPromise() {
const authTokenState = reduxStoreMain.getState().authToken
const currentNumericDate = Math.round(Date.now() / 1000)
if (authTokenState && authTokenState.token && authTokenState.payload &&
currentNumericDate + 1 * 60 <= authTokenState.payload.exp) {
//if (currentNumericDate + 3 * 60 >= authTokenState.payload.exp) getRefreshedAccessTokenPromise()
return new Promise(resolve => resolve(authTokenState.token))
}
if (!pendingAccessTokenPromise) pendingAccessTokenPromise = getRefreshedAccessTokenPromise().finally(() => pendingAccessTokenPromise = null)
return pendingAccessTokenPromise
}
export const linkTokenHeader = setContext(async (_, { headers }) => {
const accessToken = await getAccessTokenPromise()
return {
headers: {
...headers,
Authorization: accessToken ? `JWT ${accessToken}` : '',
}
}
})
export const apolloClientMain = new ApolloClient({
link: ApolloLink.from([
linkError,
linkTokenHeader,
linkMain
]),
cache: inMemoryCache
});
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
14305 次 |
最近记录: |