중요!) 2022년 6월 23일 현재 next-auth 소셜 로그인에 약간 문제가 있어. 어휴 지친다. cognito로 소셜 로그인을 하면 cognito에서는 정상적으로 로그인되지만 next-auth의 하위 모듈인 openid-client에서 nonce mismatch error를 뱉어. 이 때문에 next-auth 세션이 로그인 상태로 안가고 error 페이지로 리디렉션!
이 상태에서 한번 더 로그인을 시도하면 cognito는 이미 로그인이 되어 있는 상태여서 그런지 ㅎㅎㅎ 에러 없이 로그인 상태로 넘어가. 유저가 로그인 두번 눌러야 한다는 거지 -_-;; 암튼 그래서 현재 PR을 기다리고 있어. 👉 #4100 PR이 완료되기 전까지 사람들이 임시방편으로 patch-package를 사용해서 버티고는 있는데... 왠지... 기약이 없는 것처럼... 오픈 소스의 폐해인가??? 꽤 쓸만해 보였는데... production에 쓰면 낭패볼지도 모르겠어. 조심하자.
next-auth에서 cognito로 login을 해보려구.. firebase는 이상한 짓 많이 했어야 했는데 aws cognito는 어떨지!! 일단 amplify를 사용하고자 하는 입장에서 cognito가 별도의 작업없이 next-auth 위에서 자연스럽게 구동된다면 뭐....랄까.. firebase 버려! ㅋ
난 내가 만든 기본 템플릿 위에서부터 시작할거야👇👇 ㅎㅎ 등신같지만 자부심을 가져야 1등 등신! ㅋㅋ
https://github.com/peter-bang/nextjs-basic-configuration
이제 next-auth 설치해주자~
yarn add next-auth
그리고 메뉴얼대로 pages/api/auth폴더에 [...nextauth].ts 파일을 만들고
import NextAuth from "next-auth";
import CognitoProvider from "next-auth/providers/cognito";
export default NextAuth({
// Configure one or more authentication providers
providers: [
CognitoProvider({
clientId: process.env.COGNITO_CLIENT_ID as string,
clientSecret: process.env.COGNITO_CLIENT_SECRET as string,
issuer: process.env.COGNITO_ISSUER,
checks: "nonce",
}),
],
});
여기 cognito provider 옵션에서 checks: "nonce"는 현재 지원을 하지 않아. 이것 때문에 이 글 처음에 언급했던 에러가 발생하는데 이걸 해결하려면 patch-package를 사용해서 next-auth 모듈을 수정해야 해. 임시방편으로 굉장히 찝찝하지. 언제 PR이 완료될지도 모르고 말이야. 일단은 신경끄고 넘어가도록 하자.
이제 저기 보이는 COGNITO_CLIENT_ID, COGNITO_CLIENT_SECRET 그리고 COGNITO_ISSUER 등 환경 변수를 넣어줘야 하겠지? 우선 nextjs 루트 폴더에 .env.local 빈 파일을 생성하자구.
//.env.local 파일
COGNITO_CLIENT_ID="xxxxxxxxx"
COGNITO_CLIENT_SECRET="xxxxxxxxxxx"
COGNITO_ISSUER ="https://cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-xxxxxxxx"
COGNITO_LOGOUT_URL="http://localhost:3000/"
COGNITO_DOMAIN="https://xxxxxxxxx-staging.auth.ap-northeast-2.amazoncognito.com"
NEXTAUTH_URL="http://localhost:3000/api/auth"
NEXTAUTH_SECRET="nextauth_secret"
위에서 언급한 세가지 말고도 COGNITO_DOMAIN과 CONGNITO LOGOUT_URL이 추가로 필요해. 이것들을 얻으러 가보자.
먼저 COGNITO_CLIENT_ID, COGNITO_SECRET은 aws cognito 대쉬 보드에서 얻을 수 있어. 이전 글에서 amplify 프로젝트 잘 따라한 사람은 사용자 풀이 이미 등록이 되어 있을 것이고 아니면 새로 사용자 풀을 만들어야 하겠지?
이전글 참고👉👉 2022.06.21 - [Next.js] - next.js + AWS amplify auth login
난 이미 사용자 풀이 만들어져 있으니가 새로 만들지 않을 거야. 기존에 생성된 사용자 풀 선택 -> 앱 통합 탭 선택
앱 통합 탭 하단에 앱 클라이언트 목록이 있는데 여기에서 클라이언트 ID랑 클라이언트 secret을 얻을 수 있어. 하지만 기존에 만들어진 애들은 client secret이 없어. 그래서 새로 만들어줘야 해. 앱 클라이언트 생성 버튼 클릭.
앱 클라이언트 생성 화면에서 앱 유형: 퍼블릭 클라이언트, 앱 클라이언트 이름: 아무거나 맘대로, 클라이언트 보안키: 클라이언트 보안키 생성 선택.
호스팅 ui 설정에서 인증 후 callback url하고 허용된 logout url을 아래와 같이 입력해. 인증 후 허용된 url은 cognito 로그인 인증이 완료되면 돌아갈 콜백 URL을 말하는 거고 허용된 logout url은 마찬가지로 cognito 로그아웃이 완료되면 돌아갈 URL을 말하는 건데 허용된 URL로만 돌아갈 수 있어. 개발하는 중에는 localhost:3000으로 해야겠지?
그리고 자격 증명 공급자에서 Google을 추가해주고 그 하단에서 openID connect 범위만 조절해줬어. 전화, 이메일 등 있었는데 지웠어. 회원 가입을 email로 하면 전화번호랑 이메일을 입력
그리고 속성 읽기 및 쓰기 권한에서도 다 해제하고 조금만 남겼어. 유저한테 이만큼 정보 털어간다고 하면 누가 그 서비스 쓰겠어? 그치? 이제 다 했어. 앱 클라이언트 생성.
그럼 앱 클라이언트가 생성이 되는데 생성된 앱 클라이언트를 클릭해서 들어가보자. 그럼 거기서 앱 클라이언트 ID하고 secret을 얻을 수 있어. 이걸 복사해서 .env.local에 붙여넣기
그 다음에 COGNITO_ISSUER를 찾자. issuer는 https://cognito-idp.{region}.amazonaws.com/{PoolId} 형태로 되어 있어. region은 cognito 생성한 지역, poolid는 user poolid. 난 서울리전으로 해서 ap-northeast-2 그리고 풀 id는 cognito 사용자 풀 대시보드에서 쉽게 찾을 수 있어. 이것도 .env.local에 입력해주자.
그리고 COGNITO_LOGOUT_URL="http://localhost:3000/" 은 그대로 놔두고(cognito login 이후에 돌아갈 url) 마지막으로 COGNITO_DOMAIN인데 이건 사용자 풀 앱 통합 탭 처음에 있어.👇👇 이것도 .env.local 에 붙여넣기!
다 찾았네 ㅎㅎ 다 입력했으면 코드를 계속 짜보자구. _app.js에서 useSession hook 사용을 위해 sessionProvider로 감싸주자구.
...
import { SessionProvider } from "next-auth/react";
import { Session } from "next-auth";
...
...
interface MyAppProps extends AppProps {
emotionCache?: EmotionCache;
Component: NextPageWithLayout;
session: Session;
}
...
...
export default function MyApp(props: MyAppProps) {
const {
Component,
emotionCache = clientSideEmotionCache,
pageProps,
session,
} = props;
const getLayout = Component.getLayout ?? ((page) => page);
return getLayout(
<SessionProvider session={session}>
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
{/* <GlobalStyles /> */}
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
</SessionProvider>
);
}
그리고 components 폴더에 login-btn.tsx 파일을 생성. 👇 LoginButton이라는 컴포넌트를 간단하게 만들었어.
import { useSession, signIn, signOut } from "next-auth/react";
const LoginButton: React.FC<any> = () => {
const { data: session } = useSession();
if (session) {
return (
<>
Signed in as {session.user.email} <br />
<button
onClick={() =>
signOut({ callbackUrl: "http://localhost:3000/api/auth/logout" })
}
>
Sign out
</button>
</>
);
}
return (
<>
Not signed in <br />
<button
onClick={() =>
signIn("cognito", { callbackUrl: `${window.location.origin}` })
}
>
Sign in
</button>
</>
);
};
export default LoginButton;
여기서 잠깐! 설명하자면 헷갈리면 안돼! next-auth 콜백이랑ㅋㅋ cognito 콜백이랑 ㅋ 먼저 signOut 버튼의 콜백 url은 next-auth의 signOut이 잘 수행되면 돌아가는 콜백이야. next-auth는 signOut을 하면 session에서만 logout하고 끝나버려 cognito는 로그인 된 상태 그대로지. 이렇게 되면 유저가 다시 로그인을 할 때 묻지도 따지지도 않고 바로 로그인이 되어버리지! 이런 문제 때문에 next-auth의 signOut이 수행된 후에 cognito에서도 로그아웃을 해줘야 해. 저 콜백은 cognito 로그 아웃을 위한 api 인거야.
그럼 저 localhost:3000/api/auth/logout API endpoint를 만들어 주자. pages/api/auth 폴더에 logout.ts 파일을 만들어 주고 아래와 같이 코드 입력.👇 설명할 것도 없이 간단해. 이 api endpoint는 cognito 로그 아웃만 실행해.
async function handleLogout(req, res) {
const result = res.redirect(
`${process.env.COGNITO_DOMAIN}/logout?client_id=${process.env.COGNITO_CLIENT_ID}&logout_uri=${process.env.COGNITO_LOGOUT_URL}`
);
console.log("logout");
}
export default async function logout(req, res) {
try {
await handleLogout(req, res);
} catch (error) {
console.error(error);
res.status(error.status || 400).end(error.message);
}
}
다 했다 이제 새로만든 LogoutButton 컴포넌트만 index.ts에 삽입해서 테스트해보자규~👇
...
import LoginButton from "../components/login-btn";
...
<main>
<h1>안녕 여러분!!</h1>
<LoginButton />
</main>
...
아마 아래와 같은 현상이 나올겨. 두 번 로그인 버튼을 눌러야만 하지.
이건 이 글 초반에 말했다시피 cognito에서 정상적으로 로그인 했음에도 next-auth 하위 모듈인 openid-client에서 nonce가 발생해서 oauthcallback error 페이지로 리디렉션 했기 때문이야. 난 위에서 언급했던 것처럼 patch-package로 일부를 수정해서 해결했지만 정식 버전이 아니라는 거~ 그럼에도 불구하고 하고 싶다면 👇
우선 patch-package를 설치해줘야 해. patch-package node 모듈 커스터마이징한 후에 그거 유지시켜주는 애야. yarn install 하거나 모듈 upgrade하면 커스터마이징 한 코드들 다 날라가잖아. 안 날라가게 유지시켜주는 애야. 또 yarn을 사용하려면 postinstall-postinstall 모듈도 함께 설치해줘야 하는데 yarn remove에서도 postinstall이 수행되도록 돕는다고하네. 👇
https://www.npmjs.com/package/patch-package/v/6.4.7#why-use-postinstall-postinstall-with-yarn
자 설치 해주자 👇👇
yarn add patch-package postinstall-postinstall
그리고 package.json에서 scripts에 postinstall: "patch-package" 추가.
"scripts": {
"postinstall": "patch-package"
}
원래는 next-auth 수정하고 npx patch-package next-auth 실행해서 patches 폴더랑 파일 만들어야 하는데 너무 힘드니깐 패치 파일을 그냥 공유할게. patches 폴더 만들어서 넣자. 아! next-auth 버전 4.5.0 임 다른거 쓰면 안됌~ 다른 버전 쓸람 새로 patch파일 만들어야 함~
아래는 파일.👇
이렇게 하고 나서 로그인을 하면 정상적으로 동작하는 걸 확인할 수 있어. 휴~ 새벽까지 하다보니 자괴감이... 이런 불안정한 패키지를 써야하나... 음.... next-auth는 과연 실제로 프로젝트에 적용할 수 있나? 하는 의문이 드네. ㅎㅎ
다들 같이 고민해보쟈 ㅋㅋㅋㅋㅋㅋㅋ
아래는 github link!👇👇
https://github.com/peter-bang/nextauth-cognito
'Next.js > nextauth' 카테고리의 다른 글
nextauth 카카오 로그인 #1 (4) | 2022.07.13 |
---|---|
next.js + AWS amplify auth login (0) | 2022.06.21 |
nextauth + firebase authentication #2 (2) | 2022.06.03 |
nextauth + firebase authentication #1 (1) | 2022.06.02 |
nextauth + firebase authentication (0) | 2022.06.02 |