안녕하세요
ID/Password JWT 가입 및 로그인 구현 방법 CredentialsProvider() 알고 보자 본문
목차
1. 회원가입 구현
- 프론트에서 회원가임 폼 작성하기
- 서버에서 유효성 검사하고 DB에 데이터 저장 요청하기
2. CredentialsProvider()로 ID/Password 로그인 구현하기 (JWT 방식)
- 1) 로그인페이지로 form 생성
- 2) form을 통해 로그인 요청 시 db와 회원정보 대조
- 3) session: cookie에 session을 저장할 때 만료시간 설정
- 4) jwt callback - jwt token 생성 할 때 실행되는 코드: token에 정보 추가
- +) session callback - session 생성할 때 실행되는 코드: session에 정보 추가
회원가입 구현하기
계정을 생성하는 과정은 일반적인 방식과 똑같이 구현하면 됩니다.
1. 프론트에서 회원가임 폼 작성하기
const Register = () => {
return (
<div className={styles.container}>
<h2>닉네임/비밀번호로 로그인하기</h2>
<form method="POST" action="/api/auth/signup">
<label>
<input name="name" type="text" placeholder="닉네임" />
</label>
<label className={styles.email}>
<input name="email" type="email" placeholder="이메일" />
</label>
<label>
<input name="password" type="password" placeholder="비밀번호" />
</label>
<button type="submit">로그인</button>
<p>* 방문자는 방명록, 댓글 작성만 가능합니다.</p>
</form>
</div>
);
};
export default Register;
- id, password를 회원가입 api 로 전달합니다.
- POST 주소를 "/api/auth/signup" 으로 작성하였다면,
"/src/pages/api/auth/signup.ts" 와 같은 경로에 파일을 생성해주면 됩니다.
2. 서버에서 유효성 검사하고 DB에 데이터 저장 요청하기
import { connectDB } from "@/utils/db/db";
import { NextApiRequest, NextApiResponse } from "next";
const signUpHandler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "POST") {
let { name, email, password } = req.body;
name = name.trim();
email = email.trim();
password = password.trim();
const userData = {
name,
email,
password,
};
const db = (await connectDB).db("blog");
await db.collection("user_credentials").insertOne({ ...userData });
res.status(200).json("회원가입이 완료되었습니다.");
}
};
export default signUpHandler;
- 회원가입에 가장 기본적으로 필요한 단계는 4가지입니다.
req.body 정보 가져오기 > db 연결하기 > 회원가입 정보 DB에 저장하기 > 응답하기 - 여기에 부가적으로 유효성 검사, 중복되는 닉네임 검사 등을 추가해주면 됩니다.
🔻🔻🔻 작성 예시 펼쳐보기
CredentialsProvider()로 ID/Password 로그인 구현하기
nextauth CredentialsProvider() 설정하기
- src/pages/api/auth/[...nextauth].ts 파일에 다음과 같이 작성합니다.
mport { connectDB } from "@/utils/db/db";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcrypt";
export const authOptions = {
providers: [
CredentialsProvider({
//1. 로그인페이지로 이동하여 작성할 form을 생성
name: "credentials",
credentials: {
name: {
label: "테스터 아이디",
type: "text",
placeholder: "tester id",
},
password: { label: "테스터 비밀번호", type: "password", placeholder: "tester password" },
},
//2. form을 통해 로그인 요청시 db와 회원정보 대조
async authorize(credentials) {
let db = (await connectDB).db("blog");
let user = await db.collection("user_credentials").findOne({ name: credentials.name });
// 존재하지 않는 유저인 경우
if (!user) {
return null;
}
const isValidPassword = await bcrypt.compare(credentials.password, user.password);
// 비밀번호가 일치하지 않으면 로그인 실패
if (!isValidPassword) {
return null;
}
return user;
},
}),
],
// 3. cookie에 session token을 저장할 때 만료시간 설정
session: {
strategy: "jwt",
maxAge: 3 * 24 * 60 * 60, // 3일
},
callbacks: {
//4. jwt 생성 시 실행되는 코드
jwt: async (token: Promise<JWT>) => {
return token;
},
//5. 유저 세션이 조회될 때 session에 user 정보를 저장하여 이용할 수 있도록 함
session: async ({ session }: { session: Promise<Session> }) => {
return session;
},
},
secret: `${process.env.NEXTAUTH_SECRET}`,
adapter: MongoDBAdapter(connectDB),
};
export default NextAuth(authOptions);
주석이 달린 코드 덩어리 별로 무엇이 일어나는지 살펴봅시다.
1. 로그인페이지로 이동하여 작성할 form 생성
export const authOptions = {
providers: [
CredentialsProvider({
//1. 로그인페이지로 이동하여 작성할 form을 생성
name: "credentials",
credentials: {
name: {
label: "테스터 아이디",
type: "text",
placeholder: "tester id",
},
password: { label: "테스터 비밀번호", type: "password", placeholder: "tester password" },
},
- signin() 함수를 실행한 nextauth 로그인 페이지에서 필요한 폼을 만들 수 있습니다.
- 위의 코드대로 작성을 한 페이지에서 보여지는 내용은 아래의 사진과 같습니다.
2. form을 통해 로그인 요청시 db와 회원정보 대조
//2. form을 통해 로그인 요청시 db와 회원정보 대조
async authorize(credentials) {
let db = (await connectDB).db("blog");
let user = await db.collection("user_credentials").findOne({ name: credentials.name });
// 존재하지 않는 유저인 경우
if (!user) {
return null;
}
const isValidPassword = await bcrypt.compare(credentials.password, user.password);
// 비밀번호가 일치하지 않으면 로그인 실패
if (!isValidPassword) {
return null;
}
return user;
},
- 로그인하려는 유저 정보가 db에 존재하는지 검사하는 과정을 만들어주면 됩니다.
- null을 반환할 경우 로그인에 실패하였다는 알림을 띄워줍시다.
3. cookie에 session token을 저장할 때 만료시간 설정
// 3. cookie에 session token을 저장할 때 만료시간 설정
session: {
strategy: "jwt",
maxAge: 3 * 24 * 60 * 60, // 3일
},
- 로그인 된 유저의 브라우저에는 session-token이 발급되는데, 이때의 만료기간을 설정합니다.
4. jwt callback - jwt token 생성 할 때 실행되는 코드: token에 정보 추가 가능
callbacks: {
//4. jwt 생성 시 실행되는 코드
jwt: async ({ token }: { token: Promise<JWT> }) => {
return token;
},
- jwt callback 메서드는 JWT token을 생성하기 전 실행되는 코드입니다.
getToken() 함수가 실행될 때 전달되는 정보를 추가할 수 있습니다.
jwt: async ({ token }: { token: Promise<JWT> }) => {
token.hello = "hello"
return token;
},
출력되는 정보
const token = await getToken({ req: request });
console.log(token);
```
{
name: '비밀',
email: '비밀',
hello: 'hello',
...
}
```
The returned value will be encrypted, and it is stored in a cookie.
리턴되는 값은 암호화되어 cookie에 저장됩니다.JWT Token을 디코딩할 때 필요한 서명은 전체
코드 하단의 secret의 NEXTAUTH_SECRET 환경변수에 포함해야합니다.
secret: `${process.env.NEXTAUTH_SECRET}`,
https://next-auth.js.org/configuration/options#secret
Options | NextAuth.js
Environment Variables
next-auth.js.org
+) session callback - session 생성할 때 실행되는 코드: session에 정보 추가 가능
//5. 유저 세션이 조회될 때 session에 user 정보를 저장하여 이용할 수 있도록 함
session: async ({ session }: { session: Promise<Session> }) => {
return session;
},
},
session 방식으로 진행할 때도 마찬가지입니다.
session, jwt 타입 모듈화 하는 방법
https://next-auth.js.org/getting-started/typescript
TypeScript | NextAuth.js
NextAuth.js has its own type definitions to use in your TypeScript projects safely. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about
next-auth.js.org