1. email
- 들어가기에 앞서 굉장히 어려울 수 밖에 없는 과정이었기에 정리
- Github이 준 code를 가지고 access_token으로 교환했음
- code에는 우리가 뭘 하고자 하는 바가 명시되어 있음
- access_token은 Github API URL을 fetch하는데 사용되었고, 우리는 user의 public정보를 얻었음
- read:user를 하지 않았다면 access_token을 얻지 못했음 - access_token이 모든걸 할 수 있도록 허용한건 아니며 scope에 명시된 것만 허용해줌 - 현재 read:user를 통해 user의 정보는 잘 읽고 있으나 user:email은 잘 되고있지 않음 - 사실 이렇게 잘 안되는 이유는 강사가 access_token으로 많은걸 할수 있다는걸 강조하기 위해 일부러 이렇게 만든것 - https://docs.github.com/ko/rest/users/emails?apiVersion=2022-11-28
export const finishGithubLogin = async (req, res) => {
const baseUrl = "https://github.com/login/oauth/access_token";
const config = {
client_id: process.env.GH_CLIENT,
client_secret: process.env.GH_SECRET,
code: req.query.code,
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
// finalUrl에 POST 요청을 보내 데이터를 받아 오고
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
// json에 access_token이 있으면
if ("access_token" in tokenRequest) {
// API에 접근
const { access_token } = tokenRequest;
const apiUrl = "https://api.github.com";
const userData = await (
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData);
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(emailData);
} else {
// 아니면 login으로 돌려보냄
return res.redirect("/login");
}
};
- ES6 이전에는 fetch를 사용하면 then이 너무 많아 어지러웠지만 현재는 굉장히 보기좋게 바뀜
- log에 email이 잘 나오는데, 그 중에서도 verified이면서 primary인 것을 찾아야함
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
const email = emailData.find(
(email) => email.primary === true && email.verified === true
);
if (!email) {
// 이후 에러 notofication을 추가
return res.redirect("login");
}
} else {
// 아니면 login으로 돌려보냄 16
return res.redirect("/login");
}
- 만약 직전 코드블럭에서 16번 라인에 도착하면 primary이면서 verified인 email이 있다는 뜻이므로 user 데이터 또한 받게 됨
- 그래서 user를 로그인시킬 수도 있고, 아니면 계정을 생성시킬 수도 있음 - 왜냐하면 email이 없음(영어라 그런지 확실히 조금 이해가 안됨)
- 그리고 동일한 user email은 갖고 있지만 한명은 일반 password로 로그인하고, 다른 하나는 github로 로그인하는 user를 어떻게 처리해야 될지 모름
- 직전 문장들을 쉽게 얘기하면 홈페이지에서 회원가입을 한 user가 github로도 로그인하려고 하면 어떻게 되는지 라는것
- 또한 똑같은 email이 있다면 어떻게 할 것인지도 문제임
- 두 개의 계정을 만들어야 되는지, 아니면 통합 계정을 만들어야 되는지 고민해 봐야함
2. Login Rule(Duplicated Account)
- github에서 로그인을 시도하면 email을 주는데, 이것과 같은 홈페이지에서 가입한 email이 있다면 우린 두가지의 방법중 하나를 사용해야함
- 홈페이지에서 가입한 email로 로그인하거나, 혹은 똑같은 email이 있다는걸 알았으니 github으로 로그인 하거나
- 소셜 로그인에는 다양한 경우의 수가 있으므로 깊게 고민해 봐야함
- 여기에선 primary인 email을 받고, db에서 같은 email이 있다면 로그인시켜 줄거임
export const finishGithubLogin = async (req, res) => {
const baseUrl = "https://github.com/login/oauth/access_token";
const config = {
client_id: process.env.GH_CLIENT,
client_secret: process.env.GH_SECRET,
code: req.query.code,
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
// finalUrl에 POST 요청을 보내 데이터를 받아 오고
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
// json에 access_token이 있으면
if ("access_token" in tokenRequest) {
// API에 접근
const { access_token } = tokenRequest;
const apiUrl = "https://api.github.com";
const userData = await (
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData);
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
const emailObj = emailData.find(
(email) => email.primary === true && email.verified === true
);
console.log(emailObj);
if (!emailObj) {
// 이후 에러 notification을 추가
return res.redirect("login");
}
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = existingUser;
return res.redirect("/");
} else {
const user = await User.create({
email: emailObj.email,
socialOnly: true,
username: userData.login,
password: "",
name: userData.name ? userData.name : userData.login,
location: userData.location,
});
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
}
} else {
// 아니면 login으로 돌려보냄
return res.redirect("/login");
}
};
2-1. recap
- 배웠다시피 소셜로그인은 쓸때는 편하지만 막상 구현할때 굉장히 거지같으므로 바로 복습해봄
- github 계정 정보에 등록된 email이 db에 회원가입된 user의 email과 동일하면 바로 user가 로그인되게 만들었음
- github이 준 user의 email object를 사용하며, primary와 verified가 true인 object를 찾는다는 것
- emailObj로 User에서 동일한 email을 사용하는지 찾고, 존재하면 바로 로그인
- 존재하지 않는다면 github 계정 정보를 가지고 바로 user를 생성하고 로그인
- 소셜로그인을 구현하며 우리는 userSchema를 수정하였음
- 소셜로그인은 비밀번호가 필요없으므로 기존의 required 속성을 없애고, socialOnly=false를 default로 줬음
- 즉 socialOnly를 통해 소셜로그인은 계정을 password없이 로그인 할 수 있다는걸 알려줌
- 이제 일반 로그인을 할때 postLogin에서 username을 찾을 때 socialOnly=false인 유저만 찾아야 함
- 유저가 어떻게 로그인 했는지를 알아야 하기 때문
export const finishGithubLogin = async (req, res) => {
const baseUrl = "https://github.com/login/oauth/access_token";
const config = {
client_id: process.env.GH_CLIENT,
client_secret: process.env.GH_SECRET,
code: req.query.code,
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
// finalUrl에 POST 요청을 보내 데이터를 받아 오고
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
// json에 access_token이 있으면
if ("access_token" in tokenRequest) {
// API에 접근
const { access_token } = tokenRequest;
const apiUrl = "https://api.github.com";
const userData = await (
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData);
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
const emailObj = emailData.find(
(email) => email.primary === true && email.verified === true
);
console.log(emailObj);
if (!emailObj) {
// 이후 에러 notofication을 추가
return res.redirect("login");
}
let user = await User.findOne({ email: emailObj.email });
if (!user) {
const user = await User.create({
email: emailObj.email,
socialOnly: true,
username: userData.login,
password: "",
name: userData.name ? userData.name : userData.login,
location: userData.location,
});
}
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
} else {
// 아니면 login으로 돌려보냄
return res.redirect("/login");
}
};
- req.session이 중복되므로 그 부분을 지금과 같이 수정
- 반복되는 패턴이지만 먼저 false인지를 검증하는 패턴
3. logout
export const logout = (req, res) => {
req.session.destroy();
return res.redirect("/");
};
- db에서 확인해보면 해당 session이 사라진걸 확인할 수 있음
- 속이 뻥
- https://github.com/Daniel-Jeon/wetube-loaded/commit/b709b6590b1d3d61908492864274320e47398a25
'개발 > Node.js' 카테고리의 다른 글
Edit Profile / Protector and Public Middleware (0) | 2024.11.13 |
---|---|
Github Login Recap again again (0) | 2024.11.13 |
Github Login / Fetch / API (0) | 2024.11.12 |
Dotenv / Github Login / UrlSearchParams (0) | 2024.11.12 |
Secret Domain Expiration Etc (0) | 2024.11.12 |