๐ ์ฟ ํค๋ฅผ ์ฌ์ฉํ ์๋ ๋ก๊ทธ์ธ
- ๋ก๊ทธ์ธ ํ๋ฉด ์ฟ ํค ๋ฐํ(์ฌ์ฉ์ id ์ ๋, 60์ด์ expires ์ค์ , httpOnly ์ต์ ์ผ๊ธฐ, signed ์ต์ ์ผ๊ธฐ+์๋ฒ์ cookie-parser์ ์ํธํ ํค ์ค์ )
router.post('/', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) throw err;
if (!user) {
return res.send(
`${info.message}<br><a href="/login">๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋</a>`
);
}
req.logIn(user, (err) => {
if (err) throw err;
res.cookie('user', req.body.id, {
expires: new Date(Date.now() + 1000 * 60),
httpOnly: true,
signed: true,
});
res.redirect('/board');
});
})(req, res, next);
});
// app.js
app.use(cookieParser('ebulsok'));
- ๋ก๊ทธ์ธ์ด ์ ๋ ์ํ์ด๋ฏ๋ก express session, passport session ๋ ๋ค ์๋ ์ํ
- req.user๊ฐ undefined ์ํ์ธ๋ฐ ๊ฑฐ๊ธฐ์ ํค ๊ฐ์ ์ฐธ์กฐํ๋ ค๊ณ ํ๊ธฐ ๋๋ฌธ์ ์ค๋ฅ ๋ฐ์
- req.user๊ฐ ์๋ ๊ฒฝ์ฐ์ ํน์ ํค ๊ฐ์ ์ฐธ์กฐํ๋๋ก board.js ์ฝ๋ ์์
router.get('/', isLogin, async (req, res) => {
const client = await mongoClient.connect();
const cursor = client.db('KDT-1').collection('board');
const ARTICLE = await cursor.find({}).toArray();
const articleLen = ARTICLE.length;
res.render('board', {
ARTICLE,
articleCounts: articleLen,
userID: req.session.userID
? req.session.userID
: req.user?.id
? req.user?.id
: req.signedCookies.user,
});
});
(session์ express ๋ชจ๋์ ์คํ์ํฌ ๋ ์๋์ผ๋ก ์์ฑ๋๊ธฐ ๋๋ฌธ์ ?์ ์ถ๊ฐํ ํ์ ์์)
๐ isLogin ํจ์ ๋ชจ๋ํ
- isLogin ํจ์๋ฅผ login.js์ ์ด๋
- ํจ์๋ฅผ ๋ชจ๋๋ก ๋บ ์ ์๊ฒ ์ต๋ช ํจ์ ์ ์ธ
- isLogin์ module.exports์ ์ถ๊ฐ
const isLogin = (req, res, next) => {
if (req.session.login || req.user || req.signedCookies.user) next();
else {
res.status(300);
res.send(
'๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.<br><a href="/login">๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋</a>'
);
}
};
module.exports = { router, isLogin };
- login.js ๋ชจ๋์ ํ๋์ router๊ฐ ์๋ isLogin์ด๋ผ๋ ํจ์๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ก ๋ฐํ์ด ๋๊ธฐ ๋๋ฌธ์ app.js ์ฝ๋ ์์ ํ์
app.use('/login', loginRouter.router);
- board.js์์ ๋ชจ๋ ๋ถ๋ฌ์ค๊ธฐ
- ๊ธฐ์กด์ isLogin์ login.isLogin์ผ๋ก ๋ณ๊ฒฝ
const login = require('./login');
// ๊ธ ๋ชฉ๋ก
router.get('/', login.isLogin, async (req, res) => { ... });
// ๊ธ ๋ฑ๋ก
router.get('/write', login.isLogin, (req, res) => { ... });
router.post('/write', login.isLogin, async (req, res) => { ... });
// ๊ธ ์์
router.get('/edit/postID/:postID', login.isLogin, async (req, res) => { ... });
// ๊ธ ์ญ์
router.delete('/delete/postID/:postID', login.isLogin, async (req, res) => { ... });
๐ DOTENV
์ค์ํ ์ ๋ณด๋ฅผ ์ธ๋ถ ์ฝ๋์์ ํ์ธ์ด ๋ถ๊ฐ๋ฑํ๋๋ก ๋์์ฃผ๋ ๋ชจ๋
- [ํฐ๋ฏธ๋] npm i dotenv -s
- app.js์์ ๋ชจ๋ ํธ์ถํ๊ธฐ
require('dotenv').config();
- .env ํ์ผ์ ์ต์๋จ ํด๋์ ๋ง๋ค๊ธฐ
PORT = 4000
DB_URI = mongodb+srv://ebulsok:<password>@cluster0.mhxf9lp.mongodb.net/?retryWrites=true&w=majority
- ํ์ํ ๊ณณ์์ process.env.์ ์ฅ๋ช ์ผ๋ก ์ฌ์ฉ
// app.js
const PORT = process.env.PORT;
// mongo.js
const uri = process.env.DB_URI;
- .gitignore์ ์ถ๊ฐ
๐ OAuth(Open Authorization)
- ์ธํฐ๋ท ์ฌ์ฉ์๋ค์ด ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๊ณตํ์ง ์๊ณ ๋ค๋ฅธ ์น์ฌ์ดํธ ์์ ์์ ๋ค์ ์ ๋ณด์ ๋ํด ์น์ฌ์ดํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ๊ธฐ ์ํ ๊ฐ๋ฐฉํ ํ์ค
- ๋ณดํต ์ฌ์ฉํ๋ sns ๋ก๊ทธ์ธ!
๐ฉ 3 ์ฐธ์ฌ์
- resource server: ์ธ์ฆ์ ๋ํ ์์์ ์ค์ ๋ก ๋ณด์ ํ๊ณ ์์ผ๋ฉฐ, ์ธ์ฆ์ ์ค์ ์ ์ผ๋ก ์ฒ๋ฆฌํด์ฃผ๋ ์๋ฒ(ex. ๋ค์ด๋ฒ, ๊ตฌ๊ธ ...)
- resource owner: ์ธ์ฆ ์ ๋ณด์ ์ฃผ์ธ, ์ฆ ์ฌ์ฉ์
- client: resource server์์ ์์์ ๊ฐ์ ธ์ค๊ณ , ์ฌ์ฉ์๊ฐ ์ ์ํ๋ ค๋ ์๋น์ค
๐ฉ OAuth Flow
- resource server์ client ๋ฑ๋ก
- resource server์ ๊ฐ์ด๋์ ๋ฐ๋ผ ์ธ์ฆ ๊ตฌํ
- ์ฌ์ฉ์๊ฐ client์์ ์ธ์ฆ ์์ฒญ
- resource server์์ ์ธ์ฆ ํ ํฐ ๋ฐํ
- ์ฌ์ฉ์๊ฐ resource server์ ๋ก๊ทธ์ธ
- resource server๊ฐ ์ธ์ฆ ํ ํฐ ๊ฒ์ฆ
- ๋ก๊ทธ์ธ ์๋ฃ
๐ฉ Facebook ๋ก๊ทธ์ธ ๊ตฌํ(https://developers.facebook.com/)
- ์ฑ ๋ง๋ค๊ธฐ - ์๋น์ - ์ฑ์ ์ ํ ์ถ๊ฐ(facebook ๋ก๊ทธ์ธ) - ์น - ์ฌ์ดํธ url ์ ๋ ฅ(http://localhost:4000/)
- ์ค์ - ๊ธฐ๋ณธ ์ค์ - URL ์ ๋ ฅ ๋์ ์๋ฒ ์๋๋๋ ๋งํฌ ๊ฑธ๊ธฐ
- ์ฑ ๊ฒ์ - ๊ถํ ๋ฐ ๊ธฐ๋ฅ - public_profile - ๊ณ ๊ธ ์ก์ธ์ค ์ด์ฉํ๊ธฐ
- ๋ชจ๋ ์ค์น: [ํฐ๋ฏธ๋] npm i passport-facebook -s
- .env์ ์ธ์ฆ ์ ๋ณด ์ถ๊ฐ
FB_CLIENT =
FB_CLIENT_SECRET =
FB_CB_URL =
- ๋ชจ๋ ์ถ๊ฐ, ์ ๋ต ์๋ฆฝ(facebook profile์์ id ๊ฐ์ด DB์ ์๋ ์ฐพ์๋ณด๊ณ ๋ก๊ทธ์ธ, ์๋ค๋ฉด ํ์๊ฐ์ ํ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ)
// passport.js
const FacebookStrategy = require('passport-facebook').Strategy;
module.exports = () => {
passport.use(
new LocalStrategy( ...)
);
passport.use(
new FacebookStrategy(
{
clientID: process.env.FB_CLIENT,
clientSecret: process.env.FB_CLIENT_SECRET,
callbackURL: process.env.FB_CB_URL,
},
async (accessToken, refreshToken, profile, cb) => {
// console.log(profile);
const client = await mongoClient.connect();
const userCursor = client.db('KDT-1').collection('users');
const result = await userCursor.findOne({ id: profile.id });
if (result !== null) cb(null, result); // ํ์๊ฐ์
์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ
else {
const newUser = {
id: profile.id,
name: profile.displayName
? profile.displayName
: profile.emails[0].value,
provider: profile.provider,
};
const dbResult = await userCursor.insertOne(newUser);
if (dbResult.acknowledged) cb(null, newUser);
else cb(null, false, { message: 'ํ์ ์์ฑ์ ์คํจํ์์ต๋๋ค.' });
}
}
)
);
passport.serializeUser((user, cb) => { ... });
passport.deserializeUser(async (id, cb) => { ... });
};
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌ(์ฑ๊ณต ์ ๊ฒ์ํ์ผ๋ก, ์คํจ ์ ๋ฉ์ธํ๋ฉด์ผ๋ก)
// login.js
router.get(
'/auth/facebook',
passport.authenticate('facebook', { scope: 'email' })
);
router.get(
'/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/board',
failureRedirect: '/',
})
);
- ๊ธ์ ์ธ ๋ userName ํค๋ฅผ ์ถ๊ฐํ๋๋ก borad.js ์์
// ๊ธ ์ฐ๊ธฐ
const newArticle = {
title: req.body.title,
content: req.body.content,
userID: req.session.userID
? req.session.userID
: req.user.id
? req.user.id
: req.signedCookies.user,
postID: postID,
userName: req.user?.name ? req.user.name : req.user?.id,
};
- ์์ฑ์ ๋ถ๋ถ์์ ํด๋น ๊ธ์ด userName ํค๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉด userName, ์์ผ๋ฉด userID๋ฅผ ๋์ฐ๋๋ก board.ejs ์์
<p>์์ฑ์: <%= ARTICLE[i].userName ? ARTICLE[i].userName : ARTICLE[i].userID %></p>
๐ฉ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ๊ตฌํ(https://developers.naver.com/)
- ๋ชจ๋ ์ค์น: [ํฐ๋ฏธ๋] npm i passport-naver -s
- .env์ ์ธ์ฆ ์ ๋ณด ์ถ๊ฐ
NV_CLIENT =
NV_CLIENT_SECRET =
NV_CB_URL =
- ๋ชจ๋ ์ถ๊ฐ, ์ ๋ต ์๋ฆฝ
// passport.js
const NaverStrategy = require('passport-naver').Strategy;
passport.use(
new NaverStrategy(
{
clientID: process.env.NV_CLIENT,
clientSecret: process.env.NV_CLIENT_SECRET,
callbackURL: process.env.NV_CB_URL,
},
async (accessToken, refreshToken, profile, cb) => {
// console.log(profile);
const client = await mongoClient.connect();
const userCursor = client.db('KDT-1').collection('users');
const result = await userCursor.findOne({ id: profile.id });
if (result !== null) cb(null, result); // ํ์๊ฐ์
์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ
else {
const newUser = {
id: profile.id,
name: profile.displayName
? profile.displayName
: profile.emails[0].value,
provider: profile.provider,
};
const dbResult = await userCursor.insertOne(newUser);
if (dbResult.acknowledged) cb(null, newUser);
else cb(null, false, { message: 'ํ์ ์์ฑ์ ์คํจํ์์ต๋๋ค.' });
}
}
)
);
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
// login.js
router.get('/auth/naver', passport.authenticate('naver'));
router.get(
'/auth/naver/callback',
passport.authenticate('naver', {
successRedirect: '/board',
failureRedirect: '/',
})
);
๐ฉ ์นด์นด์ค ๋ก๊ทธ์ธ ๊ตฌํ(https://developers.kakao.com/docs/latest/ko/kakaologin/common)
์ฐธ๊ณ : passport-kakao (passportjs.org)
- ๋ชจ๋ ์ค์น: [ํฐ๋ฏธ๋] npm i passport-kakao -s
- .env์ ์ธ์ฆ ์ ๋ณด ์ถ๊ฐ
KA_CLIENT =
KA_CB_URL =
- ๋ชจ๋ ์ถ๊ฐ, ์ ๋ต ์๋ฆฝ
// passport.js
const KakaoStrategy = require('passport-kakao').Strategy;
passport.use(
new KakaoStrategy(
{
clientID: process.env.KA_CLIENT,
callbackURL: process.env.KA_CB_URL,
},
async (accessToken, refreshToken, profile, cb) => {
// console.log(profile);
const client = await mongoClient.connect();
const userCursor = client.db('KDT-1').collection('users');
const result = await userCursor.findOne({
id: profile.id,
});
if (result !== null) cb(null, result); // ํ์๊ฐ์
์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ
else {
const newUser = {
id: profile.id,
name: profile.displayName
? profile.displayName
: profile.emails[0].value,
provider: profile.provider,
};
const dbResult = await userCursor.insertOne(newUser);
if (dbResult.acknowledged) cb(null, newUser);
else cb(null, false, { message: 'ํ์ ์์ฑ์ ์คํจํ์์ต๋๋ค.' });
}
}
)
);
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
// login.js
router.get('/auth/kakao', passport.authenticate('kakao'));
router.get(
'/auth/kakao/callback',
passport.authenticate('kakao', {
successRedirect: '/board',
failureRedirect: '/',
})
);
๐ฉ ๊ตฌ๊ธ ๋ก๊ทธ์ธ ๊ตฌํ(https://console.cloud.google.com/)]
- API ๋ฐ ์๋น์ค - OAuth ๋์ ํ๋ฉด - ์ธ๋ถ - ๋ง๋ค๊ธฐ - ๋งํฌ ์ ๋ ฅ - ํ ์คํธ ์ฌ์ฉ์ ์ถ๊ฐ
- ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด - OAuth ํด๋ผ์ด์ธํธ ID - ์น ์ดํ๋ฆฌ์ผ์ด์ - ๋งํฌ ์ ๋ ฅ - ํด๋ผ์ด์ธํธ ID/PW ๋ณต์ฌ
- ๋ชจ๋ ์ค์น: [ํฐ๋ฏธ๋] npm i passport-google-oauth20
- .env์ ์ธ์ฆ ์ ๋ณด ์ถ๊ฐ
GG_CLIENT =
GG_CLIENT_SECRET =
GG_CB_URL =
- ๋ชจ๋ ์ถ๊ฐ, ์ ๋ต ์๋ฆฝ
passport.use(
new GoogleStrategy(
{
clientID: process.env.GG_CLIENT,
clientSecret: process.env.GG_CLIENT_SECRET,
callbackURL: process.env.GG_CB_URL,
},
async (accessToken, refreshToken, profile, cb) => {
console.log(profile);
const client = await mongoClient.connect();
const userCursor = client.db('KDT-1').collection('users');
const result = await userCursor.findOne({ id: profile.id });
if (result !== null) cb(null, result); // ํ์๊ฐ์
์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ
else {
const newUser = {
id: profile.id,
name: profile.displayName
? profile.displayName
: profile.emails[0].value,
provider: profile.provider,
};
const dbResult = await userCursor.insertOne(newUser);
if (dbResult.acknowledged) cb(null, newUser);
else cb(null, false, { message: 'ํ์ ์์ฑ์ ์คํจํ์์ต๋๋ค.' });
}
}
)
);
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
router.get('/auth/google', passport.authenticate('google', { scope: 'email' }));
router.get(
'/auth/google/callback',
passport.authenticate('google', {
successRedirect: '/board',
failureRedirect: '/',
})
);
๐ .gitignore ๊ฐ ์๋๋ ๊ฒฝ์ฐ: ์บ์ ์ญ์ ํ๊ธฐ
git rm -r --cached .
git add .
git commit -m "clear git cache"
git push --all
'KDT > TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
9/23 TIL : koa, pug, ์ฑํ ๊ธฐ๋ฅ ๊ตฌํ (0) | 2022.09.26 |
---|---|
9/21 TIL : multer, ์ํธํ(crypto), ์น ์์ผ์ผ๋ก ์ฑํ ์๋น์ค ๊ตฌํ (0) | 2022.09.22 |
9/16 TIL : cookie, HTTP session, passport (0) | 2022.09.20 |
9/14 TIL : ๊ฒ์ํ ๊ธฐ๋ฅ์ MongoDB ์ ์ฉ, ์๋ฒ ๋ฐฐํฌ (0) | 2022.09.20 |
9/7 TIL : form ํ๊ทธ๋ก ๋ฐ์ดํฐ ์ ๋ฌ, fetch, MongoDB (0) | 2022.09.18 |