Nextjs + Amplify Auth:用户未在 Apollo setContext 中进行身份验证

问题描述

我正在使用 Nextjs、Cognito、Amplify Auth、Apollo 客户端和自定义 Graphql 服务器。我的 Cognito 池不是使用 Amplify CLI 创建的。

我成功了

  1. 登录获取用户
  2. 在导航栏中更新用户
  3. 在我页面的 getServerSideProps 中获取服务器上的用户
  4. 页面重新加载时在导航栏中获取用户

但我没有在 apolloClient.ts 中的 setAuthorizationLink > setContext 中获取 User。它一直说用户未通过身份验证。

感谢任何帮助。

_app.ts

import { ApolloProvider } from '@apollo/client';
import { useApollo } from '../src/lib/Apollo/apolloClient';
import '../styles/globals.css';

import Amplify from 'aws-amplify';
import Navbar from '../src/Container/Navigation';
import { ThemeProvider } from '@emotion/react';
import { theme } from '../src/Components/Theme';

Amplify.configure({
  ssr: true,Auth: {
    region: 'us-east-1',userPoolId: 'x',userPoolWebClientId: 'y',cookieStorage: {
      domain: 'localhost',path: '/',expires: 365,secure: false,},});

export default function App({ Component,pageProps }) {
  const apolloClient = useApollo(pageProps);

  return (
    <ThemeProvider theme={theme}>
      <ApolloProvider client={apolloClient}>
        <Navbar />
        <Component {...pageProps} />
      </ApolloProvider>
    </ThemeProvider>
  );
}

apolloClient.ts

import { useMemo } from 'react';
import {
  ApolloClient,ApolloLink,HttpLink,InMemoryCache,normalizedCacheObject,} from '@apollo/client';
import { concatPagination } from '@apollo/client/utilities';
import merge from 'deepmerge';
import isEqual from 'lodash/isEqual';
import { setContext } from '@apollo/client/link/context';
import { Auth } from 'aws-amplify';

export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';

let apolloClient: ApolloClient<normalizedCacheObject>;

// Get Apollo Backend
const apolloBackend = new HttpLink({
  uri: 'http://localhost:4000/graphql/',credentials: 'include',});

const authLink = setContext(async (req,prev) => {
  // console.log('setContext',req);
  // // console.log('setContext',prev);
  try {
    console.log((await Auth.currentAuthenticatedUser()).getIdToken().payload);
    const session = await Auth.currentSession();
    const token = session.getAccesstoken();
    return {
      headers: { Authorization: token },};
  } catch (err) {
    console.log('authLink',err);
    return {
      headers: { Authorization: undefined },};
  }
});

function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',link: ApolloLink.from([authLink,apolloBackend]),cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            allPosts: concatPagination(),}),});
}

export function initializeApollo(initialState = null) {
  const _apolloClient = apolloClient ?? createApolloClient();

  // If your page has Next.js data fetching methods that use Apollo Client,the initial state
  // gets hydrated here
  if (initialState) {
    // Get existing cache,loaded during client side data fetching
    const existingCache = _apolloClient.extract();

    // Merge the existing cache into data passed from getStaticProps/getServerSideProps
    const data = merge(initialState,existingCache,{
      // combine arrays using object equality (like in sets)
      arrayMerge: (destinationArray,sourceArray) => [
        ...sourceArray,...destinationArray.filter((d) =>
          sourceArray.every((s) => !isEqual(d,s))
        ),],});

    // Restore the cache with the merged data
    _apolloClient.cache.restore(data);
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient;
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient;

  return _apolloClient;
}

export function addApolloState(client,pageProps) {
  if (pageProps?.props) {
    pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
  }

  return pageProps;
}

export function useApollo(pageProps) {
  const state = pageProps[APOLLO_STATE_PROP_NAME];
  const store = useMemo(() => initializeApollo(state),[state]);
  return store;
}

我.ts

import { gql } from '@apollo/client';
import { withSSRContext } from 'aws-amplify';
import React from 'react';
import {
  addApolloState,initializeApollo,} from '../src/lib/Apollo/apolloClient';
import MeComp from './meComp';

import Amplify from 'aws-amplify';
Amplify.configure({
  ssr: true,});

const me = () => {
  return <MeComp />;
};

export async function getServerSideProps(context) {
  const { Auth } = withSSRContext(context);
  const apolloClient = initializeApollo();

  const user = await Auth.currentAuthenticatedUser();
  // console.log('getServerSideProps',user);

  const query = gql`
    query {
      me {
        authenticated
        user {
          id
        }
      }
    }
  `;

  await apolloClient.query({
    query,});

  return addApolloState(apolloClient,{
    props: {},// revalidate: 1,});
}

export default me;

Navbar.ts

/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React,{ useEffect,useState } from 'react';
import Link from 'next/link';
import { Auth,Hub } from 'aws-amplify';
import { Container } from 'next/app';
import { Header,SiteNav,MenuToggle,Hamburger } from './style';
import { useRouter } from 'next/router';

const Navbar = () => {
  const [open,setopen] = useState(false);
  const [user,setUser] = useState(null);

  const router = useRouter();

  Hub.listen('auth',({ payload: { event,data } }) => {
    switch (event) {
      case 'signIn':
        console.log(1,data);
        setUser(data);
        break;
      case 'signOut':
        console.log(2,data);
        setUser(undefined);
        break;
    }
  });

  useEffect(() => {
    // Access the user session on the client
    Auth.currentAuthenticatedUser()
      .then((user) => {
        console.log('User: ',user);
        setUser(user);
      })
      .catch((err) => {
        console.log('Error:',err);
        setUser(null);
      });
  },[]);

  const authenticated = user ? true : false;

  return (
    <Header data-test='header'>
      <Container>
        <SiteNav open={open}>
          <ul>
            <li data-test='home'>
              <Link href='/'>
                <a onClick={() => setopen(!open)}>Home</a>
              </Link>
            </li>
            <li data-test='me'>
              <Link href='/me'>
                <a onClick={() => setopen(!open)}>Me</a>
              </Link>
            </li>
           ...
            {authenticated ? (
              <li data-test='cockpit'>
                <Link href='/cockpit'>
                  <a onClick={() => setopen(!open)}>Cockpit</a>
                </Link>
              </li>
            ) : null}
            {!authenticated ? (
              <li data-test='register'>
                <Link href='/auth/register'>
                  <a onClick={() => setopen(!open)}>Register</a>
                </Link>
              </li>
            ) : null}
            {!authenticated ? (
              <li data-test='login'>
                <Link href='/auth/login'>
                  <a onClick={() => setopen(!open)}>Login</a>
                </Link>
              </li>
            ) : null}
            {authenticated ? (
              <li data-test='logout'>
                <a
                  onClick={async () => {
                    try {
                      await Auth.signOut();
                      setopen(!open);
                      router.push('/');
                    } catch {
                      return;
                    }
                  }}
                >
                  logout
                </a>
              </li>
            ) : null}
          </ul>
        </SiteNav>
        {authenticated ? <span>{`Logged in as: ${user.username}`}</span> : null}
        <MenuToggle onClick={() => setopen(!open)}>
          <Hamburger open={open} />
        </MenuToggle>
      </Container>
    </Header>
  );
};

export default Navbar;

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)