diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..597409667cb067712c946c3392291e2a46a199f1 --- /dev/null +++ b/.env @@ -0,0 +1,10 @@ +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=0000 +DB_NAME=meeting +MONGO_URI=mongodb://localhost:27017/your_mongo_db +PORT=8080 + +GOOGLE_CLIENT_ID=552188448115-ice40pervgq9s0me3sme59fjpsia7sm2.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=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 +CALLBACK_URL=http://localhost:8080/auth/google/callback \ No newline at end of file diff --git a/app.js b/app.js index 30e4db353850f08d3fc669d8d2d7a747c4ecee45..310461c6182cf4315418f78f372ea1e6c780a30c 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,47 @@ +<<<<<<< Updated upstream +======= +// app.js + +require('dotenv').config(); + +>>>>>>> Stashed changes const express = require('express'); +const session = require('express-session'); +const passport = require('./passport'); // 변경된 경로 +const flash = require('connect-flash'); + const app = express(); +<<<<<<< Updated upstream +======= + +// 미들웨어 설정 +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); + +// 세션 설정 +app.use( + session({ + secret: 'your_session_secret', + resave: false, + saveUninitialized: false, + }) +); + +// Passport 초기화 및 세션 연결 +app.use(passport.initialize()); +app.use(passport.session()); + +// 플래시 메시지 (선택 사항) +app.use(flash()); + +// 라우트 설정 +const authRoutes = require('./routes/auth'); +app.use('/auth', authRoutes); + +// ... 다른 라우트 및 설정 + +// 서버 시작 +>>>>>>> Stashed changes const PORT = process.env.PORT || 3000; app.get('/', (req, res) => { diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..8ae9be046f8afd9efcacc14ed35648cad6fb1206 --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,15 @@ +// middlewares/auth.js + +exports.isLoggedIn = (req, res, next) => { + if (req.isAuthenticated()) { + return next(); + } + res.redirect('/auth/login'); +}; + +exports.isNotLoggedIn = (req, res, next) => { + if (!req.isAuthenticated()) { + return next(); + } + res.redirect('/'); +}; diff --git a/package-lock.json b/package-lock.json index 1a8403b9545523b866368e3750e2702dcfbd7e3c..5b6f0cb241482de4514b474d918b3220c9f7acf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "connect-flash": "^0.1.1", "cookie-parser": "^1.4.7", "dotenv": "^16.4.5", "express": "^4.21.1", @@ -17,6 +18,8 @@ "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "sequelize": "^6.37.5", "sequelize-cli": "^6.6.2" }, @@ -207,6 +210,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -528,6 +540,14 @@ "proto-list": "~1.2.1" } }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1971,6 +1991,12 @@ "node": ">=0.10.0" } }, + "node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2028,6 +2054,64 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "license": "MIT", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "license": "MIT", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2071,6 +2155,11 @@ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "license": "MIT" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/pg-connection-string": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", @@ -2822,6 +2911,12 @@ "node": ">= 0.8" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==", + "license": "MIT" + }, "node_modules/umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", diff --git a/package.json b/package.json index e756213aec4f3e54bc1225e7d5bdc7be05bb2eb5..9f213ea1e4c71887a51fae3e303004fa9f5afeda 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "ISC", "description": "", "dependencies": { + "connect-flash": "^0.1.1", "cookie-parser": "^1.4.7", "dotenv": "^16.4.5", "express": "^4.21.1", @@ -21,6 +22,8 @@ "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "sequelize": "^6.37.5", "sequelize-cli": "^6.6.2" }, diff --git a/passport/googleStrategy.js b/passport/googleStrategy.js new file mode 100644 index 0000000000000000000000000000000000000000..c5964eff4a79c6fddcea7b7a9d5cb6b07ea583c4 --- /dev/null +++ b/passport/googleStrategy.js @@ -0,0 +1,29 @@ +// passport/googleStrategy.js + +const { Strategy: GoogleStrategy } = require('passport-google-oauth20'); +const User = require('../models/User'); + +module.exports = new GoogleStrategy( + { + clientID: process.env.GOOGLE_CLIENT_ID, // .env 파일에 설정 + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.CALLBACK_URL, + }, + async (accessToken, refreshToken, profile, done) => { + try { + // 프로필에서 사용자 정보 추출 + const email = profile.emails[0].value; + const name = profile.displayName; + + // 데이터베이스에서 사용자 찾거나 생성 + let user = await User.findOne({ where: { email } }); + if (!user) { + user = await User.create({ email, name }); + } + + return done(null, user); + } catch (err) { + return done(err); + } + } +); diff --git a/passport/index.js b/passport/index.js new file mode 100644 index 0000000000000000000000000000000000000000..6f12584b083ce566a93354123eb47740084be6b9 --- /dev/null +++ b/passport/index.js @@ -0,0 +1,25 @@ +// passport/index.js + +const passport = require('passport'); +const User = require('../models/User'); + +// 직렬화 +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +// 역직렬화 +passport.deserializeUser(async (id, done) => { + try { + const user = await User.findByPk(id); + done(null, user); + } catch (err) { + done(err); + } +}); + +// 전략 가져오기 +const googleStrategy = require('./googleStrategy'); +passport.use(googleStrategy); + +module.exports = passport; diff --git a/routes/auth.js b/routes/auth.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7eda249d0f05a64d3537bc0462e2da4f7c6fd831 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -0,0 +1,35 @@ +// routes/auth.js + +const express = require('express'); +const passport = require('passport'); + +const router = express.Router(); + +// GET /auth/login +router.get('/login', (req, res) => { + res.send('<a href="/auth/google">Log in with Google</a>'); +}); + +// GET /auth/logout +router.get('/logout', (req, res) => { + req.logout(() => { + res.redirect('/'); + }); +}); + +// GET /auth/google +router.get( + '/google', + passport.authenticate('google', { scope: ['profile', 'email'] }) +); + +// GET /auth/google/callback +router.get( + '/google/callback', + passport.authenticate('google', { failureRedirect: '/auth/login' }), + (req, res) => { + res.redirect('/'); + } +); + +module.exports = router;