#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>
)
}