首页 > 解决方案 > 如何使用 Hasura 后端在 Next.JS 应用程序中使用 JWT 令牌配置 Apollo 客户端?

问题描述

我在使用 Hasura GraphQL 数据库的 Next.JS 项目中使用 JWT 令牌配置 Apollo 客户端时遇到问题。

奇怪的是,有时数据会加载;有时,查询会出错(见底部)。

我这样设置我的 Apollo 客户端:

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  uri: process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL,
  cache: new InMemoryCache(),
  fetchOptions: {
    mode: 'no-cors',
  },
});

export default client;

在 pages/_app.js 中,我调用 client 作为 ApolloProvider 的道具:

return (
    <UserContext.Provider value={[user, setUser]}>
      <ApolloProvider client={client}>
        <Component {...pageProps} />
      </ApolloProvider>
    </UserContext.Provider>
  );

当用户登录主页时,会调用以下 API 函数,存储在 pages/api/login 中:

import {Magic} from '@magic-sdk/admin'
import CookieService from '../../lib/cookie'
import jwt from 'jsonwebtoken'; 

let magic = new Magic(process.env.MAGIC_SECRET_KEY)

export default async (req, res) => {
    if (req.method !== 'POST') return res.status(405).end()
  
    // exchange the DID from Magic for some user data
    const did = magic.utils.parseAuthorizationHeader(req.headers.authorization)
    const user = await magic.users.getMetadataByToken(did);

    // create jwt token
    let token = jwt.sign(
        {
            ...user,
            'https://hasura.io/jwt/claims': {
                'x-hasura-allowed-roles': ['user'],
                'x-hasura-default-role': 'user',
                'x-hasura-user-id': `${user.issuer}`,
            },
            exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * process.env.SESSION_LENGTH_IN_DAYS,
        },
        process.env.JWT_SECRET
    );

    // Author a couple of cookies to persist a users session
    CookieService.setTokenCookie(res, token)
  
    res.json(user); 
    res.end();
}

这是 CookieService.setTokenCookie 函数:

function setTokenCookie(res, token) {
  res.setHeader("Set-Cookie", [
    createCookie(TOKEN_NAME, token),
    createCookie("authed", true, { httpOnly: false }),
  ])
}

我创建了一个挂钩来访问 JWT 令牌:

import useSWR from "swr";

const fetchUser = url =>
  fetch(url)
    .then(r => r.json())
    .then(data => {
      return { user: data?.user || null };
});

export function useUser() {
  const { data, error } = useSWR('/api/user', fetchUser);
  return !data && !error ? { loading: true } : data && data?.user;
}

在应用程序的各个页面中,我调用 useUser 并使用 useQuery 挂钩来查询数据库:

const user = useUser();

const { data, loading, error } = useQuery(GetData, 
  {
    context: {
      headers: {
        Authorization : `Bearer ${token}`
      }
    },
  }
);

通过这种实现,我有时能够从后端接收数据,但经常会收到以下错误:

Error: Could not verify JWT: JWSError (CompactDecodeError Invalid number of parts: Expected 3 parts; got 1)
    at new ApolloError (index.js?3ca0:31)
    at QueryData.getExecuteResult (QueryData.js?74a3:213)
    at QueryData.execute (QueryData.js?74a3:44)
    at eval (useBaseQuery.js?b9a7:32)
    at useDeepMemo (useDeepMemo.js?31e9:6)
    at useBaseQuery (useBaseQuery.js?b9a7:32)
    at useQuery (useQuery.js?f933:3)
    at Offerings (index.js?3ad2:26)
    at renderWithHooks (react-dom.development.js?61bb:14985)
    at updateFunctionComponent (react-dom.development.js?61bb:17356)
    at beginWork (react-dom.development.js?61bb:19063)
    at beginWork$1 (react-dom.development.js?61bb:23940)
    at performUnitOfWork (react-dom.development.js?61bb:22776)
    at workLoopSync (react-dom.development.js?61bb:22707)
    at renderRootSync (react-dom.development.js?61bb:22670)
    at performSyncWorkOnRoot (react-dom.development.js?61bb:22293)
    at eval (react-dom.development.js?61bb:11327)
    at unstable_runWithPriority (scheduler.development.js?3069:468)
    at runWithPriority$1 (react-dom.development.js?61bb:11276)
    at flushSyncCallbackQueueImpl (react-dom.development.js?61bb:11322)
    at flushSyncCallbackQueue (react-dom.development.js?61bb:11309)
    at scheduleUpdateOnFiber (react-dom.development.js?61bb:21893)
    at dispatchAction (react-dom.development.js?61bb:16139)
    at eval (useBaseQuery.js?b9a7:18)

如果您对如何解决此错误有任何提示,请告诉我!

标签: jwtnext.jsapollo-clienthasura

解决方案


推荐阅读