aws-amplify-реагирует, и nextjs нарушает мой пользовательский контекст

#reactjs #next.js #amazon-cognito #vercel #amplify

Вопрос:

Я не могу понять, почему, но когда я использую cognito с моим собственным пользовательским контекстом, все работает просто отлично, но как только я использую компонент более высокого порядка withAuthenticator, он нарушает мой пользовательский контекст, и я ни за что на свете не могу понять, почему или даже как это исправить. Я опубликую свой файл пользовательского контекста ниже для справки и расскажу вам, где он ломается.

 import { Auth } from 'aws-amplify'
import {createContext, useState, useEffect, useMemo} from 'react'

//TODO must redo cognito from scratch and will probably be able to keep this user context untouched

export const UserContext = createContext(null)

export const UserProvider = ({children}) => {
  
  const [ user, setUser ] = useState(null)
  const [ userEmail, setUserEmail ] = useState(null)
  const [ signInError, setSignInError ] = useState(false)

  useEffect(()=>{
    // AWS Cognito
    Auth.currentAuthenticatedUser().then(x=>setUser(x)).catch((err)=>setUser(null))
  },[])

   const handleSignInError = () => {
    console.log(signInError)
  }

  const login = (username, password) => {
    signInError amp;amp; setSignInError(false)
    Auth.signIn(username, password)
    .then( x => {
      setUser(x)
      console.log('Welcome: '   x.challengeParam.userAttributes.email)
      setUserEmail(x.challengeParam.userAttributes.email)
      setSignInError(false)
    })
    .catch((err)=>{
      console.log(err.code)
      if(err.code === 'UserNotFoundException' || 'NotAuthorizedException'){
        err.message = 'Invalid username or password'
        setSignInError(true)
        console.log(err.message)
      }
    })  
  }
  
  const logout = () => {
    Auth.signOut().then((x)=>{
      setUser(null)
      setUserEmail(null)
      return x
    })
  }

  const signup = (username, email, password) => {
    Auth.signUp({ username, password, attributes: { email } })
    .then( x => {
      setUser(x)
      return x
    })
    .catch((err)=>{
      if(err.code){
        err.message = 'Your Username or Password was incorrect'
      }
      throw err
    })
  }
  
  const vals = useMemo( () => ({user, login, logout, signup, handleSignInError, userEmail, signInError}), [user, userEmail, signInError])
  
  return(
    <UserContext.Provider value={vals}>
      {children}
    </UserContext.Provider>
  )
}
 

В функции входа в систему теперь он возвращает пользователя, которого не нашли после того, как я завернул компонент и npm i aws-amplify-react . Самое забавное, что когда я удаляю его, я все равно получаю ту же ошибку и не могу вернуться, не удалив полностью amplify и не пройдя полную amplify init снова. Еще более запутанным является то, что мое приложение размещено на vercel, и оно ломается после того, как я пытаюсь сделать это на своем локальном компьютере. Если я ничего не упускаю, и мое приложение действительно ломается в облаке, даже если я не нажимаю свой измененный код, то я предполагаю, что cognito получает что-то в облаке, когда я пытаюсь это сделать на своем локальном компьютере, а затем портит мою нетронутую копию на vercel????? С тех пор я также пытался использовать next-auth, что заставляет меня думать, что я должен просто придерживаться фронтальной работы или найти лучшее решение? любая помощь будет признательна. Я вернусь к своей старой настройке, восстановлю свой cognito и усилюсь с нуля, чтобы начать все сначала.

Ответ №1:

Вам необходимо позвонить в Cognito configure, прежде чем звонить своему поставщику аутентификации. Поместите его перед определением поставщика аутентификации или контекста.

 Auth.configure({...your_config})
const UserContext = () => {};
 

Я также использую крюк аутентификации в своем контексте, который устраняет необходимость в специальном.

 import { useContext } from 'react';
export const useAuth = () => useContext(UserContext);

// use it in components and pages
const user = useAuth();
 

Убедитесь, что в вашей конфигурации используются все соответствующие типы. Если вы этого не сделаете, это иногда молча терпит неудачу. Например, файлы ENV всегда передаются в виде строк, поэтому некоторые параметры должны быть приведены к соответствующему типу, например, срок действия файлов cookie истекает

 {
  authenticationFlowType: 'USER_SRP_AUTH',
  cookieStorage: {
    ...other settings
    expires: Number(process.env.NEXT_PUBLIC_COGNITO_COOKIE_EXPIRES),
  }
};
 

Вам также нужно будет вызвать Auth.configure на каждой странице, на которой вам нужен доступ к проверке подлинности Congito внутри getStaticPaths, getStaticProps и getServerSideProps. Это связано с тем, что они независимо вызываются из вашего приложения во время сборки или на сервере.

 Auth.configure({...your_config})
const getStaticProps = () => {};
const getStaticPaths = () => {};
const getServerSideProps = () => {};
 

Если вы можете использовать его, их размещенный пользовательский интерфейс довольно хорош.

Наконец, у AWS есть несколько библиотек для усиления, и я использую @aws-amplify/auth их — я не знаю, имеет ли это значение.

Комментарии:

1. Спасибо тебе за это. Ваш ответ вместе с учебником здесь dev.to/dabit3/the-complete-guide-to-next-js-authentication-2aco поставь меня на место.

Ответ №2:

Я добавил файл конфигурации в свой _app.js и установите ssr: true для аутентификации ssr

 import Amplify from 'aws-amplify'
import config from '../src/aws-exports'

Amplify.configure({...config, ssr: true})
 

Вот мой рабочий пользовательский контекст. Я удалил функцию регистрации и добавлю ее позже, как только поработаю над ней и протестирую.

 import { Auth } from 'aws-amplify'
import {createContext, useState, useEffect, useMemo} from 'react'

export const UserContext = createContext(null)

export const UserProvider = ({children}) => {
  
  const [ user, setUser ] = useState(null)
  const [ userEmail, setUserEmail ] = useState(null)
  const [ signInError, setSignInError ] = useState(false)
  const [sub, setSub] = useState(null)
  useEffect(()=>{
    // AWS Cognito
    Auth.currentAuthenticatedUser()
      .then(x=>{
        setUser(x.username)
        setUserEmail(x.attributes.email)
        setSub(x.attributes.sub)
      })
      .catch((err)=>{
        console.log(err)
        setUser(null)
      })
  },[])

   const handleSignInError = () => {
    console.log(signInError)
  }

  const login = (username, password) => {
    signInError amp;amp; setSignInError(false);
    Auth.signIn(username, password)
      .then((x) => {
        setUser(x.username)
        setSignInError(false)
        console.log(x)
      })
      .catch((err)=>{
        console.log(err)    
        setSignInError(true)
      })  
  }
  
  const logout = () => {
    Auth.signOut().then((x)=>{
      setUser(null)
      setUserEmail(null)
      setSub(null)
    })
  }

  
  }
  
  const vals = useMemo( () => ({user, sub, login, logout, handleSignInError, userEmail, signInError}), [user, userEmail, signInError, sub])
  
  return(
    <UserContext.Provider value={vals}>
      {children}
    </UserContext.Provider>
  )
}