Notice
Recent Posts
Link
정화 코딩
[Node.js] Google OAuth 2.0으로 로그인/로그아웃 구현 본문
구글 개발자 도구 웹페이지에서 구글 클라이언트 생성
https://console.cloud.google.com/apis/dashboard
index.js
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import session from 'express-session';
import FileStore from 'session-file-store';
import passport from 'passport';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const app = express();
// console.log(`The current environment is: ${process.env.NODE_ENV}`);
app.use(express.json());
import authRouter from './routes/authRoutes.js';
import userRouter from './routes/userRoutes.js';
import placeRouter from './routes/placeRoutes.js';
import travelRouter from './routes/travelRoutes.js';
import reviewRouter from './routes/reviewRoutes.js';
// 라우트 설정
app.use('/auth', authRouter);
app.use('/users', userRouter);
app.use('/places', placeRouter);
app.use('/travels', travelRouter);
app.use('/reviews', reviewRouter);
const fileStore = FileStore(session);
const sessionSecret = process.env.SESSION_SECRET;
// 미들웨어 설정
app.use(session({
secret: sessionSecret,
resave: false,
saveUninitialized: false,
store: new fileStore({ path: './sessions' }) // 세션 파일 저장 경로 설정
}));
// Passport 미들웨어 초기화
app.use(passport.initialize());
app.use(passport.session());
// Passport의 직렬화 및 역직렬화 설정
// serializeUser : 로그인 / 회원가입 후 1회 실행
// deserializeUser : 페이지 전환시 마다 실행
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
// 홈페이지 생성 (req.user는 passport의 직렬화된 사용자 정보가 저장됨)
app.get('/', (req, res) => {
const temp = getPage('Welcome', 'Welcome to visit...', getBtn(req.user));
res.send(temp);
});
// 로그인/로그아웃 상태에 따른 버튼 생성
const getBtn = (user) => {
return user !== undefined ? `${user.name} | <a href="/auth/logout">logout</a>` : `<a href="/auth/google">Google Login</a>`;
}
// 페이지 생성 함수
const getPage = (title, description, auth) => {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
</head>
<body>
${auth}
<h1>${title}</h1>
<p>${description}</p>
</body>
</html>
`;
}
// 서버 시작
app.listen(3000, () => console.log('http://localhost:3000'));
authRoutes.js
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const authRouter = express.Router();
authRouter.use(express.json());
import session from 'express-session';
import FileStore from 'session-file-store';
import passport from 'passport';
import { OAuth2Strategy as GoogleStrategy } from 'passport-google-oauth';
// Google Client 관련 정보
const googleClientId = process.env.GOOGLE_CLIENT_ID;
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
const fileStore = FileStore(session);
const sessionSecret = process.env.SESSION_SECRET;
// 미들웨어 설정
authRouter.use(session({
secret: sessionSecret,
resave: false,
saveUninitialized: false,
store: new fileStore({ path: './sessions' }) // 세션 파일 저장 경로 설정
}));
authRouter.use(passport.initialize());
authRouter.use(passport.session());
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
// 구글 API ID, Secret 정보 저장 (구글 개발자 웹사이트에서 발급받은 클라이언트 ID와 시크릿 입력)
const googleCredentials = {
"web": {
"client_id": googleClientId,
"client_secret": googleClientSecret,
"redirect_uris": [
"http://localhost:3000/auth/google/callback"
]
}
}
// Passport (Google) - 구글 로그인시 정보 GET
passport.use(new GoogleStrategy({
clientID: googleCredentials.web.client_id,
clientSecret: googleCredentials.web.client_secret,
callbackURL: googleCredentials.web.redirect_uris[0]
},
async (accessToken, refreshToken, profile, done) => {
try {
// 사용자 정보가 이미 데이터베이스에 있는지 확인
let user = await prisma.user.findUnique({
where: { email: profile.emails[0].value },
});
if (user) {
// 사용자 정보 업데이트
user = await prisma.user.update({
where: { id: user.id },
data: {
provider: profile.provider,
providerId: profile.id,
token: accessToken,
name: profile.displayName,
},
});
} else {
// 새로운 사용자 정보 추가
user = await prisma.user.create({
data: {
provider: profile.provider,
providerId: profile.id,
token: accessToken,
name: profile.displayName,
email: profile.emails[0].value,
},
});
}
return done(null, user);
} catch (error) {
return done(error, null);
}
}
));
// 구글 로그인 버튼 클릭 시 구글 인증 페이지로 이동
authRouter.get('/google',
passport.authenticate('google', { scope: ['email', 'profile'] }));
// 구글 로그인 후 콜백 URL (원래 웹사이트로 돌아옴)
authRouter.get('/google/callback',
passport.authenticate('google', { failureRedirect: '/auth/login' }),
(req, res) => res.redirect('/'));
// 로그아웃 페이지: 로그아웃 처리, 세션 삭제 및 쿠키 삭제 후 홈으로 리다이렉션
// passport 패키지 내 req.logout()으로 로그아웃 기능 구현
authRouter.get('/logout', (req, res, next) => {
req.logout((err) => {
if (err) {
return next(err); // 에러가 발생하면 next(err)로 에러 핸들러로 전달
}
req.session.destroy((err) => {
if (err) {
return next(err); // 에러가 발생하면 next(err)로 에러 핸들러로 전달
}
res.clearCookie('connect.sid');
res.redirect('/');
});
});
});
// 에러 핸들러
authRouter.use((err, req, res, next) => {
if (res.headersSent) {
return next(err); // 이미 헤더가 전송된 경우, 다음 에러 핸들러로 전달
}
res.status(500).send(err.message || 'Internal Server Error');
});
export default authRouter;
[참고] https://goodmemory.tistory.com/97
'Web Development' 카테고리의 다른 글
[Node.js] NestJS이란? (0) | 2024.09.09 |
---|---|
[Back-end] AWS EC2 인스턴스를 이용한 백엔드 배포 (0) | 2024.09.07 |
[Node.js] jwt 토큰으로 회원가입/로그인/로그아웃 구현 (0) | 2024.07.22 |
[Back-end] codeit : 프로그래밍과 데이터 in JavaScript (0) | 2024.05.21 |
[Back-end] codeit : 프로그래밍 핵심 개념 in JavaScript (0) | 2024.05.21 |
Comments