reactjs 处理firebase初始化延迟和自定义apollographql后端的id令牌

zlhcx6iw  于 2023-01-12  发布在  React
关注(0)|答案(1)|浏览(113)

目前,当我使用firebase验证用户时,我会将他们的auth令牌存储在localStorage中,以便稍后连接到我的后端,如下所示:

const httpLink = new HttpLink({uri: 'http://localhost:9000/graphql'})

const authMiddleware = new ApolloLink((operation, forward) => {
  // add the authorization token to the headers
  const token = localStorage.getItem(AUTH_TOKEN) || null
  operation.setContext({
    headers: {
      authorization: token ? `Bearer ${token}` : ''
    }
  })
  return forward(operation)
})

const authAfterware = onError(({networkError}) => {
  if (networkError.statusCode === 401) AuthService.signout()
})

function createApolloClient() {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: authMiddleware.concat(authAfterware).concat(httpLink)
  })
}

我遇到的问题是,令牌过期后我无法刷新它,所以我尝试使用下面的代码来设置apollo的授权令牌:

const httpLink = new HttpLink({uri: 'http://localhost:9000/graphql'})

const asyncAuthLink = setContext(
  () => {
    return new Promise((success, reject) => {
      firebase.auth().currentUser.getToken().then(token => {
        success({
          headers: {
            authorization: token ? `Bearer ${token}` : ''
          }
        })
      }).catch(error => {
        reject(error)
      })
    })
  }
)

const authAfterware = onError(({networkError}) => {
  if (networkError.statusCode === 401) AuthService.signout()
})

function createApolloClient() {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: asyncAuthLink.concat(authAfterware.concat(httpLink))
  })
}

这在用户第一次认证时有效,但一旦用户刷新页面,当我的graphql查询发送到我的后端时,firebase就不再初始化,所以令牌不会随它一起发送。是否有一种方法可以异步地等待firebase.auth().currentUser,这样就可以正常工作?或者是否有另一种方法可以完全采用?据我所知(100%肯定)currentUser.getIdToken只在当前令牌不再有效时才进行网络调用。我认为这是可以接受的,因为在令牌无效的情况下,后端无论如何都无法响应,所以我需要等待令牌刷新才能继续。
我想到了一些其他的想法:

  • 继续使用localStorage来存储auth令牌,如果我的后端发送回401响应,则在authAfterware中刷新它,并重试请求。
  • 设置获取身份验证令牌的延迟(不可取)
  • 还有别的主意吗?

谢谢!

z9smfwbn

z9smfwbn1#

我知道有点晚了,但我也被困在这个问题上,并找到了解决它的方法。也许这不是最好的方法,但至少它是有效的。我的方法是创建一个Next API端点来使用getUserFromCookies方法检索用户令牌:

import { NextApiRequest, NextApiResponse } from "next";
import { getUserFromCookies } from "next-firebase-auth";
import initAuth from "../../utils/initAuth";

initAuth();

const handler = async (req: NextApiRequest, res: NextApiResponse<any>) => {
  try {
    const user = await getUserFromCookies({ req, includeToken: true });
    const accessToken = await user.getIdToken();
    return res.status(200).json({ success: true, accessToken });
  } catch (e) {
    console.log(`${e}`);
    return res.status(500).json({ error: `Unexpected error. ${e}` });
  }
};

export default handler;

然后在apollo客户端配置中调用此端点,如下所示:

import { ApolloClient, InMemoryCache, ApolloLink, HttpLink, concat } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { relayStylePagination } from "@apollo/client/utilities";

const getUserToken = async () => {
  const res = await fetch("http://localhost:3000/api/get-user-token");
  const { accessToken } = await res.json();
  return accessToken;
};

const asyncAuthLink = setContext(async (request) => {
  const token = await getUserToken();
  return { ...request, headers: { authorization: token ? `Bearer ${token}` : "" } };
});

const httpLink = new HttpLink({ uri: process.env.NEXT_PUBLIC_API_URL });

const client = new ApolloClient({
  name: "web",
  version: "1.0",
  uri: process.env.NEXT_PUBLIC_API_URL,
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          users: relayStylePagination(),
        },
      },
    },
  }),
  link: concat(asyncAuthLink, httpLink),
});

export default client;

希望能有所帮助!

相关问题