회원기능 만들기 아이디/비번 + JWT 사용
03 May 2023 | nextjs아이디/비번 방식으로 로그인하려면,
Next-auth
라이브러리 설정에서 Credentials provider를 선택하면 된다.
다만 이 경우 session 방식 말고 강제로 JWT방식만 사용하도록 셋팅 되어있기때문에, 기존에 세션방식으로 짠 코드가있다면 다 무용지물이된다.
라이브러리 제작자 말로는 ‘개발자가 직접 아이디,비밀번호를 관리하면 위험하니 사용못하게 막아놨다’고 한다.
과정은 두가지가 끝이다.
- 회원가입 페이지에서 아이디/비번을 서버로 제출
- 서버는 그걸 DB에 저장
끝.
회원가입페이지에서 유저가 아이디와 비밀번호 제출
// /app/register/page.js;
export default function Register() {
return (
<div>
<form method='POST' action='/api/auth/signup'>
<input name='name' type='text' placeholder='이름' />
<input name='email' type='text' placeholder='이메일' />
<input name='password' type='password' placeholder='비번' />
<button type='submit'>id/pw 가입요청</button>
</form>
</div>
);
}
회원가입페이지다.
이름,이메일,패스워드를 입력후 서버에 전송하는 페이지다.
요즘은 아이디말고 이메일 쓰는게 유행이라고하는데, OAuth로 로그인시에도 유저 아이디는 없고 이메일만 나와서 이메일 쓰는건 일종의 일관성 유지 용도도 있다고한다.
참고로 이메일말고, 아이디만 써도 상관은없지만, OAuth는 중복방지위해서 이메일이 필요하기때문에 email란도 있어야 아마 나중에 문제가 없을거라고 한다. (소셜로그인 기능 구현안할거면 상관없다.)
서버는 그걸 DB에 저장
npm install bcrypt
암호화 라이브러리인 bcrypt를 이용해서 DB에 비밀번호를 암호화시켜서 저장해주자.
// /pages/api/auth/signup.js;
import { connectDB } from '@/util/database';
import bcrypt from 'bcrypt';
export default async function handler(요청, 응답) {
if (요청.method === 'POST') {
const hash = await bcrypt.hash(요청.body.password, 10);
요청.body.password = hash;
let db = (await connectDB).db('forum');
await db.collection('user_cred').insertOne(요청.body);
응답.status(200).json('성공');
}
}
폼으로 전송된 유저정보를 DB에 저장하는 서버 기능 코드이다.
bcrypt를 이용해서 비밀번호를 암호화 시켜줬고,
유저 정보를 object로 만들어서 user_cred 컬렉션에 넣어줬다.
Credentials provider 설정하기
Next-auth 설정에 Credentials provider도 설정해놓으면 아이디/비밀번호 로그인이 가능하다.
복붙.
import { connectDB } from '@/util/database';
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: [
GithubProvider({
clientId: 'Github에서 발급받은 ID',
clientSecret: 'Github에서 발급받은 Secret',
}),
// 이부분부터 추가
CredentialsProvider({
//1. 로그인페이지 폼 자동생성해주는 코드
name: 'credentials',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'password', type: 'password' },
},
//2. 로그인요청시 실행되는코드
//직접 DB에서 아이디,비번 비교하고
//아이디,비번 맞으면 return 결과, 틀리면 return null 해야함
async authorize(credentials) {
let db = (await connectDB).db('forum');
let user = await db
.collection('user_cred')
.findOne({ email: credentials.email });
if (!user) {
console.log('해당 이메일은 없음');
return null;
}
const pwcheck = await bcrypt.compare(
credentials.password,
user.password
);
if (!pwcheck) {
console.log('비번틀림');
return null;
}
return user;
},
}),
],
//3. jwt 써놔야 잘됩니다 + jwt 만료일설정
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, //30일
},
callbacks: {
//4. jwt 만들 때 실행되는 코드
//user변수는 DB의 유저정보담겨있고 token.user에 뭐 저장하면 jwt에 들어갑니다.
jwt: async ({ token, user }) => {
if (user) {
token.user = {};
token.user.name = user.name;
token.user.email = user.email;
}
return token;
},
//5. 유저 세션이 조회될 때 마다 실행되는 코드
// 컴포넌트 안에서 보여줄 유저 정보, 토큰에있는 유저들을 컴포넌트 안에서 보여준다고 생각하면됨
session: async ({ session, token }) => {
session.user = token.user;
return session;
},
},
adapter: MongoDBAdapter(connectDB),
secret: 'qwer1234',
};
export default NextAuth(authOptions);
라이브러리 사용법이라서 외우고 공부할 필요X
그대로 복붙해서 사용하고 어떤 용드의 코드인지만 잘 살펴볼것
주의깊게 봐야할건 2번
이다. 로그인시 아이디/비번을 DB에 저장된 내용과 비교하는건 내가 직접 해야하니깐, 나의 DB 상황에 맞게 수정해서 쓰면 되겠다.