전체소스 코드
https://github.com/kimfame/next-s3
이전 아이디, 패스워드 기반 로그인 포스트에 이어서
https://bonevillain.tistory.com/31
구글 로그인을 추가를 해보았다.
사전 진행사항
1. Google Clould Platform에서 프로젝트 생성
2. OAuth 동의 화면 (OAuth consent screen)
- 앱 이름, 사용자 지원 이메일, 개발자 연락처 정보 입력 (필수값)
- 구글 로그인 진행하면서 보여줄 동의 화면 꾸미는 과정있음
3. 사용자 인증 정보 (Credentials)
- Authorised redirect URIs 필드에
http://localhost:3000/api/auth/callback/google 추가 필수
- 이 부분에서 구글 로그인 인증 이용을 위한 키 아이디, 비밀키 제공 (.env.local 추가 필요)
로그인 API (Google Provider 추가)
/src/app/api/auth/[...nextauth]/route.js
export const authOptions = {
secret: process.env.SECRET,
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
CredentialsProvider({
name: 'Credentials',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'test@example.com',
},
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
const email = credentials?.email
const password = credentials?.password
await connect()
const user = await User.findOne({ email })
const passwordOk = user && bcrypt.compareSync(password, user.password)
if (passwordOk) {
return user
}
return null
},
}),
],
callbacks: {
async session({ session }) {
const newSession = session
const sessionUser = await User.findOne({ email: session.user.email })
newSession.user.id = sessionUser._id
return newSession
},
async signIn({ account, profile }) {
if (account.provider === 'google') {
if (!profile.email_verified) {
return false
}
try {
await connect()
const userExists = await User.findOne({ email: profile.email })
if (!userExists) {
await User.create({
email: profile.email,
name: profile.name,
})
}
return true
} catch (error) {
console.log(error)
}
} else if (account.provider === 'credentials') {
return true
}
return false
},
},
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
- providers에 GoogleProvider 추가하고 키 아이디와 비밀키 넣어준다. Github 등 다른 제3자 인증도 비슷한 방식으로 가능하다.
- 구글에 로그인 성공하면 서버에서 회원가입처럼 회원정보를 저장해야한다. 이 때, callbacks 안의 signIn 함수 안에서 해당 부분을 작성해주면 된다.
signIn으로 넘어오는 파라미터가 몇 개 있는데 대표적으로 사용하는 것이 account, profile, user 정보가 되는 것 같다.
account는 보통 provider 값을 통해 제3자 인증들 중에서 어떤 것을 사용하였는지 구분할 때 사용한다.
if (account.provider === 'google') {}
if (account.provider === 'credentials') {}
각 인증에 맞춰서 로직을 넣어주면 되고 return 값은 Boolean인 true, false가 된다.
유튜브 강의 중에 어떤 사람은 User 객체를 넘겨주는 사람도 있던데 공식 문서 예제에 따르면 true, false로 넘겨준다.
signIn 성공하면 true, 실패하면 false.
구글 계정 테스트 기준 provider 값 외에 type, providerAccountId, access_token, expires_at, scope, token_type, id_token 필드가 있다.
profile의 경우, 저장할 사용자 정보인 이름과 이메일 정보 등이 담겨있으므로 해당 정보에서 빼내면 된다. user 객체에도 있는 것 같은데 필요에 따라 맞춰서 끌어다가 쓰면 될 것 같다.
(구글 계정 테스트 기준 profile 데이터 필드 : iss, azp, aud, sub, email, email_verified, at_hash, name, given_name, iat, exp)
- Credentials Provider 쪽은 signIn에 대해서 별도 처리가 없어도된다. 로그인 인증도 그렇고 사용자 등록도 Provider 안의 authorize 안에서 다 처리하기 때문이다. 하지만 관련하여 추가 로직이 없다면 명시적으로 signIn 함수 안에서 Credentials Provider 부분을 return true로 해줘야한다. 여기서 false 반환하게 되면 로그인을 정상적으로 진행하였어도 아래 그림과 같이 최종 로그인 실패로 되기 때문이다.
추가로 당연하겠지만 순서는 CredentialsProvider 내의 authorize가 먼저 실행되고 그 다음 callbacks의 signIn이 실행된다.
로그인폼 안에 구글 로그인 버튼 추가
/src/app/login/page.jsx
'use client'
import { signIn } from 'next-auth/react'
import { useState } from 'react'
export default function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
async function handleFormSubmit(ev) {
ev.preventDefault()
await signIn('credentials', { email, password, callbackUrl: '/' })
}
return (
<section>
<h1>Login</h1>
<form onSubmit={handleFormSubmit}>
<input
type="email"
name="email"
placeholder="email"
value={email}
onChange={(ev) => setEmail(ev.target.value)}
/>
<input
type="password"
name="password"
placeholder="password"
value={password}
onChange={(ev) => setPassword(ev.target.value)}
/>
<button type="submit">Login</button>
<button
type="button"
onClick={() => signIn('google', { callbackUrl: '/' })}
>
Google Login
</button>
</form>
</section>
)
}
- 실질적으로 버튼 하나 추가한 것이 전부이긴 하다.
<button type="button" onClick={() => signIn('google', { callbackUrl: '/' })}>
Google Login
</button>
(나중에 필요해서 소스코드를 다시 볼 때, 소스 가독성을 높이려고 CSS를 사용하지 않았는데 테스트할 때 UI 거슬린다.)
[참조]
https://next-auth.js.org/providers/google
https://mongoosejs.com/docs/connections.html
https://www.youtube.com/watch?v=_Nm133UZ50U
https://www.youtube.com/watch?v=I_YCC_nFt70
'React > Next.js' 카테고리의 다른 글
[Next.js] AWS S3 ACL Private 특정 유저만 접근 허용 (0) | 2024.03.28 |
---|---|
[Next.js] AWS S3 이미지 업로드 예제 (2) - presigned URL (0) | 2024.03.25 |
[Next.js] AWS S3 이미지 업로드 예제 (1) (0) | 2024.03.22 |
[Next.js] 아이디, 비밀번호 로그인 예제 (0) | 2024.03.18 |
[Next.js] 프로젝트 생성 / 개발 서버 실행 (0) | 2024.03.11 |