将 NextAuth JWT 传递给 Apollo 客户端

问题描述 投票:0回答:1

我正在创建一个 nextjs 应用程序,它利用 NextAuth 进行身份验证并生成具有自定义编码和解码的 JWT。传递 hasura 声明需要自定义编码和解码。

如何将该 jwt 传递给 apollo,以便在请求标头中附加所述 jwt,以便 hasura 验证我的请求?我看了几个例子,人们从本地存储中提取令牌,但 NEXTAuth 将 JWT 存储在 cookie 中。我不知道如何访问它。 我尝试将令牌添加到我的会话中并使用 NextAuth getSession() 方法读取会话,但它返回 null。

lib pollo.ts

import {
  ApolloClient,
  InMemoryCache,
  ApolloLink,
  HttpLink,
  concat,
} from '@apollo/client'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getSession } from 'next-auth/react'

// TODO - Replace URIs with environment variables

async function sesFunc() {
  const sesh = await getSession()
  return sesh
}
var ses = sesFunc()
console.log(`Session in apollo: ${JSON.stringify(ses)}`)


const graphLink = new HttpLink({
  uri: 'http://localhost:8080/v1/graphql',
})

//GraphQL Relay Endpoint
const relayLink = new HttpLink({
  uri: 'http://localhost:8080/v1beta1/relay',
})

const wsLink =
  typeof window !== 'undefined'
    ? new WebSocketLink({
        uri: 'ws://localhost:8080/v1/graphql',
        options: {
          reconnect: true,
        },
      })
    : undefined
const authMiddleware = new ApolloLink((operation, forward) => {
  //const { data: session, status } = useSession()

  // console.log(`AuthMiddle Session: ${session}`)
  operation.setContext(({ headers = {} }) => ({
    headers: {
      ...headers,
      Authorization:
        'Bearer <token>',
    },
  }))
  return forward(operation)
})

const apolloClient = new ApolloClient({
  link: concat(
    authMiddleware,
    ApolloLink.split(
      (operation) => operation.getContext().clientName === 'relayLink',
      relayLink,
      ApolloLink.split(
        (operation) => operation.getContext().clientName === 'graphLink',
        graphLink,
        wsLink
      )
    )
  ),
  cache: new InMemoryCache(),
})

export default apolloClient

_app.tsx

import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { SessionProvider } from 'next-auth/react'
import IdleTimerContainer from '../components/IdleTimerContainer'
import Layout from '../components/Layout'
import { ApolloProvider } from '@apollo/client'
import apolloClient from '../lib/apollo'

function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <SessionProvider session={session}>
      <ApolloProvider client={apolloClient}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </ApolloProvider>
      <IdleTimerContainer />
    </SessionProvider>
  )
}

export default MyApp
next.js jwt apollo-client next-auth
1个回答
0
投票

这是一个将自定义 NextAuth JWT 传递到 Apollo 客户端的解决方案,适用于服务器和客户端组件。此设置允许您从 NextAuth 会话安全地检索 JWT 并将其包含在 Apollo 客户端标头中以进行 Hasura 身份验证。

服务器端选项:

如果使用服务器组件,请使用

registerApolloClient
创建客户端并添加一个函数以从 NextAuth 会话中检索令牌。

// src/lib/client.ts
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support";

const getToken = async () => {
  // Use an absolute URL on the server environment, otherwise a relative URL on the client
  const baseUrl = typeof window === 'undefined' ? process.env.NEXTAUTH_URL : '';
  const res = await fetch(`${baseUrl}/api/auth/session`);

  if (!res.ok) {
    throw new Error('Error getting session');
  }

  const session = await res.json();
  return session?.access_token; // Return the token, or adjust as necessary
};

export const { getClient } = registerApolloClient(() => {
  const token = getToken();
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: new HttpLink({
      uri: "http://localhost:3001/graphql",
      headers: {
        authorization: `Bearer ${token}`,
      },
    }),
  });
});

客户端选项: 如果使用客户端组件,请设置 ApolloWrapper 组件来管理令牌检索并将其传递到每个请求的标头中。

// src/app/ApolloWrapper.tsx
"use client";

import { ApolloNextAppProvider, ApolloClient, InMemoryCache } from "@apollo/experimental-nextjs-app-support";
import { HttpLink } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

const getToken = async () => {
    // Retrieve the token from the session
    const res = await fetch('/api/auth/session'); // Get session from NextAuth
    const session = await res.json();
    return session.accessToken; // Return the access token
};

function makeClient() {
    const httpLink = new HttpLink({
        uri: "http://localhost:3001/graphql", // Adjust according to your setup
        fetchOptions: { cache: "no-store" },
    });

    const authLink = setContext(async (_, { headers }) => {
        const token = await getToken(); // Retrieve the token
        return {
            headers: {
                ...headers,
                Authorization: token ? `Bearer ${token}` : "", // Add the Bearer token if it exists
            },
        };
    });

    return new ApolloClient({
        cache: new InMemoryCache(),
        link: authLink.concat(httpLink), // Concatenate the authLink with the httpLink
    });
}

export function ApolloWrapper({ children }: React.PropsWithChildren) {
    return <ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>;
}

然后将其传递给您的app.tsx或layout.tsx

// _app.tsx or layout.tsx
import type { AppProps } from 'next/app';
import { SessionProvider } from 'next-auth/react';
import { ApolloWrapper } from '../src/app/ApolloWrapper';

function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <SessionProvider session={session}>
      <ApolloWrapper>
        <Component {...pageProps} />
      </ApolloWrapper>
    </SessionProvider>
  );
}

export default MyApp;

我正在使用 next js 14 和 apollo 客户端实验用于应用程序路由,但如果它适用于页面路由。

参考:ApolloGraphqlApollo 客户端身份验证

© www.soinside.com 2019 - 2024. All rights reserved.