Flare-k

[Authenticate] Github Login

...@@ -118,3 +118,5 @@ dist ...@@ -118,3 +118,5 @@ dist
118 package-lock.json 118 package-lock.json
119 uploads 119 uploads
120 static 120 static
121 +text
122 +*.txt
...\ No newline at end of file ...\ No newline at end of file
......
1 +import dotenv from "dotenv";
1 import express from "express"; 2 import express from "express";
2 import morgan from "morgan"; 3 import morgan from "morgan";
3 import helmet from "helmet"; 4 import helmet from "helmet";
...@@ -12,9 +13,9 @@ import routes from "./routes"; ...@@ -12,9 +13,9 @@ import routes from "./routes";
12 import userRouter from "./routers/userRouter"; 13 import userRouter from "./routers/userRouter";
13 import videoRouter from "./routers/videoRouter"; 14 import videoRouter from "./routers/videoRouter";
14 import globalRouter from "./routers/globalRouter"; 15 import globalRouter from "./routers/globalRouter";
15 -
16 import "./passport"; 16 import "./passport";
17 17
18 +dotenv.config();
18 const app = express(); 19 const app = express();
19 20
20 const CokieStore = MongoStore(session); 21 const CokieStore = MongoStore(session);
...@@ -36,6 +37,7 @@ app.use( ...@@ -36,6 +37,7 @@ app.use(
36 store: new CokieStore({ mongooseConnection: mongoose.connection }), 37 store: new CokieStore({ mongooseConnection: mongoose.connection }),
37 }) 38 })
38 ); 39 );
40 +
39 app.use(passport.initialize()); 41 app.use(passport.initialize());
40 app.use(passport.session()); 42 app.use(passport.session());
41 43
......
...@@ -15,7 +15,7 @@ export const postJoin = async (req, res, next) => { ...@@ -15,7 +15,7 @@ export const postJoin = async (req, res, next) => {
15 res.render("join", { pageTitle: "Join" }); 15 res.render("join", { pageTitle: "Join" });
16 } else { 16 } else {
17 try { 17 try {
18 - const user = await User.create({ 18 + const user = await User({
19 name, 19 name,
20 email, 20 email,
21 }); 21 });
...@@ -33,16 +33,57 @@ export const postJoin = async (req, res, next) => { ...@@ -33,16 +33,57 @@ export const postJoin = async (req, res, next) => {
33 33
34 export const getLogin = (req, res) => 34 export const getLogin = (req, res) =>
35 res.render("login", { pageTitle: "Login" }); 35 res.render("login", { pageTitle: "Login" });
36 +
36 export const postLogin = passport.authenticate("local", { 37 export const postLogin = passport.authenticate("local", {
37 failureRedirect: routes.login, 38 failureRedirect: routes.login,
38 successRedirect: routes.home, 39 successRedirect: routes.home,
39 }); 40 });
40 41
42 +// 깃허브 로그인
43 +export const githubLogin = passport.authenticate("github");
44 +// call back 받아오는 함수
45 +export const githubLoginCallback = async (
46 + accessToken,
47 + refreshToken,
48 + profile,
49 + cb
50 +) => {
51 + // callback 정보 확인용 console.log
52 + // console.log(accessToken, refreshToken, profile, cb);
53 + const {
54 + _json: { id, avatar_url: avatarUrl, name, email },
55 + } = profile;
56 + // profile 파라미터로 가져온 내용들
57 + // 파라미터로 받은 cb함수가 처리해준다. True->정보를 쿠키에.. False->error
58 + try {
59 + const user = await User.findOne({ email });
60 + if (user) {
61 + user.githubId = id;
62 + user.save();
63 + return cb(null, user);
64 + }
65 + const newUser = await User.create({
66 + email,
67 + name,
68 + githubId: id,
69 + avatarUrl,
70 + });
71 + return cb(null, newUser);
72 + } catch (error) {
73 + return cb(error, null);
74 + }
75 +};
76 +
77 +export const postGithubLogin = (req, res) => {
78 + res.redirect(routes.home);
79 +};
80 +
41 // 로그아웃을 클릭하면 LogOut페이지로 가는 것 대신에, 로그아웃을 처리한 후 81 // 로그아웃을 클릭하면 LogOut페이지로 가는 것 대신에, 로그아웃을 처리한 후
42 // home 페이지로 Redirect로 표현할 것이다. 82 // home 페이지로 Redirect로 표현할 것이다.
43 // 즉, 초반에 만들어둔 logout.pug는 삭제해도 좋다. 83 // 즉, 초반에 만들어둔 logout.pug는 삭제해도 좋다.
44 export const logout = (req, res) => { 84 export const logout = (req, res) => {
45 // res.render("logout", { pageTitle: "Logout" }); 85 // res.render("logout", { pageTitle: "Logout" });
86 + req.logout();
46 res.redirect(routes.home); 87 res.redirect(routes.home);
47 }; 88 };
48 89
......
1 +dotenv.config();
1 import dotenv from "dotenv"; 2 import dotenv from "dotenv";
2 import app from "./app"; // app.js에서 export default app했기 때문에 불러올 수 있다. 3 import app from "./app"; // app.js에서 export default app했기 때문에 불러올 수 있다.
3 import "./db"; 4 import "./db";
...@@ -5,8 +6,6 @@ import "./models/Video"; ...@@ -5,8 +6,6 @@ import "./models/Video";
5 import "./models/Comment"; 6 import "./models/Comment";
6 import "./models/User"; 7 import "./models/User";
7 8
8 -dotenv.config();
9 -
10 const PORT = process.env.PORT || 80; 9 const PORT = process.env.PORT || 80;
11 10
12 const handleListening = () => { 11 const handleListening = () => {
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
35 "multer": "^1.4.2", 35 "multer": "^1.4.2",
36 "node-sass": "^4.14.1", 36 "node-sass": "^4.14.1",
37 "passport": "^0.4.1", 37 "passport": "^0.4.1",
38 + "passport-github": "^1.1.0",
38 "passport-local": "^1.0.0", 39 "passport-local": "^1.0.0",
39 "passport-local-mongoose": "^6.0.1", 40 "passport-local-mongoose": "^6.0.1",
40 "postcss-loader": "^3.0.0", 41 "postcss-loader": "^3.0.0",
......
1 +import dotenv from "dotenv";
1 import passport from "passport"; 2 import passport from "passport";
3 +import GithubStrategy from "passport-github";
2 import User from "./models/User"; 4 import User from "./models/User";
5 +import { githubLoginCallback } from "./controllers/userController";
6 +import routes from "./routes";
3 7
8 +dotenv.config();
4 // passport에게 strategy(로그인 방식)를 사용하도록 요청한다. 9 // passport에게 strategy(로그인 방식)를 사용하도록 요청한다.
5 // passportLocalMongooser가 제공하는 strategy를 이용한다. -> username과 password를 사용. 10 // passportLocalMongooser가 제공하는 strategy를 이용한다. -> username과 password를 사용.
6 passport.use(User.createStrategy()); 11 passport.use(User.createStrategy());
7 12
13 +passport.use(
14 + new GithubStrategy(
15 + {
16 + clientID: process.env.GH_ID,
17 + clientSecret: process.env.GH_SECRET,
18 + callbackURL: `http://localhost:80${routes.githubCallback}`,
19 + },
20 + githubLoginCallback
21 + )
22 +);
23 +
8 passport.serializeUser(User.serializeUser()); 24 passport.serializeUser(User.serializeUser());
9 passport.deserializeUser(User.deserializeUser()); 25 passport.deserializeUser(User.deserializeUser());
......
1 import express from "express"; 1 import express from "express";
2 +import passport from "passport";
2 import routes from "../routes"; 3 import routes from "../routes";
3 import { home, search } from "../controllers/videoController"; 4 import { home, search } from "../controllers/videoController";
4 import { 5 import {
5 - logout,
6 getJoin, 6 getJoin,
7 - postJoin,
8 getLogin, 7 getLogin,
8 + logout,
9 + postJoin,
9 postLogin, 10 postLogin,
11 + githubLogin,
12 + postGithubLogin,
10 } from "../controllers/userController"; 13 } from "../controllers/userController";
11 -import { onlyPublic } from "../middlewares"; 14 +import { onlyPublic, onlyPrivate } from "../middlewares";
12 15
13 const globalRouter = express.Router(); 16 const globalRouter = express.Router();
14 17
...@@ -20,5 +23,15 @@ globalRouter.post(routes.login, onlyPublic, postLogin); ...@@ -20,5 +23,15 @@ globalRouter.post(routes.login, onlyPublic, postLogin);
20 23
21 globalRouter.get(routes.home, home); 24 globalRouter.get(routes.home, home);
22 globalRouter.get(routes.search, search); 25 globalRouter.get(routes.search, search);
23 -globalRouter.get(routes.logout, logout); 26 +globalRouter.get(routes.logout, onlyPrivate, logout);
27 +
28 +globalRouter.get(routes.gitHub, githubLogin);
29 +// callback으로 받아온 정보를 다뤄야하는 함수도 필요하다.
30 +// callback으로 가면 passport.authenticate 처리 해줘야함
31 +globalRouter.get(
32 + routes.githubCallback,
33 + passport.authenticate("github", { failureRedirect: "/login" }),
34 + postGithubLogin
35 +);
36 +
24 export default globalRouter; 37 export default globalRouter;
......
...@@ -18,6 +18,10 @@ const VIDEO_DETAIL = "/:id"; ...@@ -18,6 +18,10 @@ const VIDEO_DETAIL = "/:id";
18 const EDIT_VIDEO = "/:id/edit"; 18 const EDIT_VIDEO = "/:id/edit";
19 const DELETE_VIDEO = "/:id/delete"; 19 const DELETE_VIDEO = "/:id/delete";
20 20
21 +// Github
22 +const GITHUB = "/auth/github";
23 +const GITHUB_CALLBACK = "/auth/github/callback";
24 +
21 const routes = { 25 const routes = {
22 home: HOME, 26 home: HOME,
23 join: JOIN, 27 join: JOIN,
...@@ -57,6 +61,8 @@ const routes = { ...@@ -57,6 +61,8 @@ const routes = {
57 return DELETE_VIDEO; 61 return DELETE_VIDEO;
58 } 62 }
59 }, 63 },
64 + gitHub: GITHUB,
65 + githubCallback: GITHUB_CALLBACK,
60 }; 66 };
61 // template에서 직접 접근이 필요한 경우 함수로 바꿔준다. 67 // template에서 직접 접근이 필요한 경우 함수로 바꿔준다.
62 export default routes; 68 export default routes;
......
...@@ -3,3 +3,25 @@ express session을 설치한다. npm install express-session ...@@ -3,3 +3,25 @@ express session을 설치한다. npm install express-session
3 postJoin은 이메일과 비밀번호를 전달하고 next()가 호출되어 postLogin으로 간다. 3 postJoin은 이메일과 비밀번호를 전달하고 next()가 호출되어 postLogin으로 간다.
4 4
5 connect mongo를 통해 저장소를 생성한다. 5 connect mongo를 통해 저장소를 생성한다.
6 +
7 +- 인증에 대한 정리 -
8 +유저네임과 비밀번호를 이용한 방식(local 방식)은 비교적 간단하다. 유저네임과 비밀번호를 post 방식으로 전달하고
9 +설치해준 플러그인인 Mongoose가 자동으로 체크를 해준다. 만약 비밀번호가 맞으면 passport에게 맞다고 알려주고
10 +passport는 쿠키를 생성한다.
11 +
12 +소셜로그인의 경우에는 조금 다르다
13 +먼저 사용자는 깃허브 사이트로 이동하게 되고 거기에서 권한 승인을 한다.
14 +그 이후에 깃헙 사이트는 우리에게 해당 사용자의 정보를 보내주는데 /auth/github/callback이라는 URL로 오게 된다
15 +그렇게 되면 passport가 함수를 호출하는데 githubLoginCallback이라는 우리가 만들어준 함수이다.
16 +passport가 이함수를 실행하는 것이다.
17 +이 함수는 모든 사용자 프로필 정보를 받고 이 정보로 필요한 것을 할 수 있다.
18 +githubLoginCallback 함수의 한가지 조건은 callback(cb) 함수를 리턴해야하는 것이다.
19 +cb 함수를 실행시켜서 에러가 있는지 유저가 있는지 알려줘야 한다.
20 +
21 +에러가 존재하면 passport는 에러가 있구나 유저는 없구나하고 끝내고
22 +유저가 존재하면 passport는 이 유저를 가져와 쿠키를 만들고 저장한다.
23 +이렇게 저장된 쿠키를 브라우저로 보내게 된다.
24 +
25 +globalRouter에서 깃허브로 갈때 githubLogin이 실행되는데 깃허브로 보내주는 역할을 한다.
26 +그리고 githubCallback(URL)로 돌아왔을 때 passport는 사용자가 알려준 함수인 githubLoginCallback을 실행시킨다.
27 +만약 user를 찾으면 passport는 통과시키며 postGithubLogin을 실행하고 홈으로 리다이렉트한다.
......
1 .social-login 1 .social-login
2 button.social-login--github 2 button.social-login--github
3 + a(href=routes.gitHub)
3 span 4 span
4 i.fab.fa-github 5 i.fab.fa-github
5 | Continue with Github 6 | Continue with Github
......