저번 파트에서 뭘했는지 살펴 보기로 한다.
Github
가 준 코드를 가지고 access_token
으로 교환을 했다.
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,
};
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}`;
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
const userRequest = await (
await fetch("https://api.github.com/user", {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userRequest);
} else {
return res.redirect("/login");
}
access_token
은 Github API URL
을 fetch
하는데 사용 되었다.
그랬더니 얻은 건 user
의 public
정보였다. 이건 scope
로 요청했기 때문에 가능했다.
만일 read:user
를 요청하지 않았다면 받은 코드로는 user
의 정보를 읽을 수 있는
access_token
을 받을 수 없을거다.
어찌 되었든 Github API
에 대해 얘기하고 있고 Github API
로 가면 무얼 할수 있는지 전부 볼수 있다.
https://docs.github.com/en/rest/users/users#get-the-authenticated-user
예를 들면 현재 한것 처럼 user
를 불러올수 있다.
이 부분이 user
를 가져오고 있는거다. 다른 많은 것들로 할수 있다.
user
를 차단 시킬수도 있고 user
를 팔로우 할수도 있다.
이것들을 한번 해보는 것도 좋을 것 같다. PUT request
를 /user/following/{username}
으로 보낸다 하면
지금은 이걸 허용하는 access_token
이 없으니 작동하진 않을거다.
access_token
이 모든걸 할수 있도록 허용하는건 아니기 때문이다.
scope
에 적은 내용에 대해서만 허용해줄 뿐이다.
다시 말해 access_token
은 user
가 모든걸 할수 있게 해주진 않는다.
scope
에 명시하면 Github
가 코드를 줄거다.
그 코드에는 이미 하고자 하는 바가 명시 되어 있다.
그리고 그 코드를 access_token
으로 바꾸게 되고 access_token
은 한다고 한 것 하게 된다.
현재 경우에서 access_token
을 가지고 하려는건 첫번째 scope
이다.
export const startGithubLogin = (req, res) => {
const baseUrl = "https://github.com/login/oauth/authorize";
const config = {
client_id: process.env.GH_CLIENT,
allow_signup: false,
scope: "read:user user:email",
};
user
의 정보를 읽어 들이고 있다. 하지만 아직 두번째 요구인 email
을 읽어 들이고 있지 않다.
이건 access_token
을 가지고 더 많은 걸 할수 있다는걸 보여주기 위해 일부러 이렇게 만들어 본거다.
여기서 user
의 데이터를 가져오고 있으니까
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
const userRequest = await (
await fetch("https://api.github.com/user", {
headers: {
Authorization: `token ${access_token}`,
},
})
userRequest
를 userData
로 바꿔 준다.
const userData = await (
await fetch("https://api.github.com/user", {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData);
어떤 user
들은 public
(공개된) email
을 가지고 있을 수도 있다.
하지만 이 경우엔 고의로 이렇게 만들었다. public email
이 아닌걸로 말이다.
그렇기 때문에 user
의 email
을 요청하기 위해 똑같은 access_token
을 써야 한다.
Github API
를 잠깐 살펴보면 email
만 다른 부분을 볼수 있다.
https://docs.github.com/en/rest/users/emails
primary email
설정하기는 현재 상태랑 관계없다. 모든 email
주소를 부여주기가 현재 필요한 거다.
그러면 모든 email
주소들을 가져오도록 한다.
if ("access_token" in tokenRequest) {
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 {
return res.redirect("/login");
}
};
fetch
에서 뒷부분은 편리하게 사용 할수 있게 만들어 준다.
이제 user
데이터를 볼러 올수 있다는걸 안다. 하지만 이제 email
데이터도 원한다.
await
부분도 반복되기에 그대로 복사해서 넣어주고 url
일부분 수정해 준다.
그리고 console.log(emailData)
를 해준다.
이제 두개의 request
를 하고 있다. 그리고 당연히 json
도 넣어 준다.
이렇게 하면 짧게 쓸 수 있다. fetch
를 하고 await json()
을 쓰는 거다.
이제 email
데이터를 사용할 준비가 된거다. 그럼 콘솔로 돌아가서 다시 한번 시도한다.
이제 두개의 데이터를 받았다. 하나는 이미 알고 있는 공개된 프로필이고
다른 하나는 email
들이다. 이제 email
이 verified
이면서 primary
인 것들을 찾아야한다.
그러니 email
들 중 verified
(확인)이면서 primary
(일순위)인 걸 찾아 볼거다.
왜냐하면 가끔 Github
으로 계정 생성을 해도 primary
혹은 verified
가 안 되었을수도 있다.
user
의 email
을 얻고 있다. 다시 말하자면 /user/emails
와
/user
로 보내는 이 request
들은 access_token
이 볼수 있게끔 허락해줬기 때문에 작동하는거다.
모든건 scope
부분에서 출발한다. 현재 이 경우가 제일 익숙하지 않다.
await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
ES6
에 익숙지 않아서 그렇다. 그런데 이 두 부분은 지금 써보려는 방식과 비슷하다.
fetch(x).then(response => response.json()).then(json => )
await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
하지만 이렇게 작성하는건 좋지 못하다. 왜냐하면 이 말인 즉슨 .then
안으로 들어가야 하기 때문이다.
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
예를 들어 이 부분에서 access_token
을 얻으면 그렇다는건 여기에서 access_token
을 얻는다는 거다.
fetch(x).then(response => response.json()).then(json => access_token)
await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
그럼 이줄에서 fetch
를 한번더 해야 한다는 소리이다.
fetch(x).then(response => response.json()).then(json => access_token fetch ())
왜냐하면 이것도 가져와야 하기 때문이다.
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
})
결국 fetch
를 써야 하는데 만일 여기서 했던 것처럼 똑같이 하고 싶다면
이렇게 해야 될거다.
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
}).then(response => response.json()).then(json => {
if ("access_token" in json) {
const { access_token } = tokenRequest;
const apiUrl = "https://api.github.com";
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
}).then(response => response.json()).then(json => {
fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
});
}
});
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
if ("access_token" in tokenRequest) {
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 {
return res.redirect("/login");
}
};
이것들을 전부 fetch
하고 then
을 쓰는 거다. 맨위 부터 시작해서 then
안에
then
, 또 then
안에 then
그리고 또 fetch
를 한다.
이거 완전 이상한 짓이다. 이걸 user email
이랑 user
프로필에도 해줘야 하는데
그러다간 머리가 터질지도 모른다.
그래서 then
을 쓰는 대신에 그냥 원래대로 하는거다.너무나 당연히 이 방법이 훯씬 좋다.
어쨋든 user
데이터를 불러오고 있고, email
도 불러오고 있다.
이제 user
데이터랑 email
데이터를 가지고 있으니까 유저의 Github ID
도 알고 있다는거고,
user
의 email
들도 알고 있다는 거다.
이제 primary
와 verified
가 모두 true
인 email
을 찾아야 한다.
그래서 이 부분을 콘솔로 복붙해서 사용해 본다.
[
{
email: 'pkpanda@naver.com',
primary: true,
verified: true,
visibility: 'private'
},
{
email: '91738673+JooMercury@users.noreply.github.com',
primary: false,
verified: true,
visibility: null
}
]
const emails = [
{
email: 'pkpanda@naver.com',
primary: true,
verified: true,
visibility: 'private'
},
{
email: '91738673+JooMercury@users.noreply.github.com',
primary: false,
verified: true,
visibility: null
}
]
2개의 email
을 가진 배열을 만들었다. 그런 다음에 이렇게 입력해 본다.
emails.find(email => email.primary === true && email.verified === true)
{email: 'pkpanda@naver.com', primary: true, verified: true, visibility: 'private'}
emails.find()
을 써서 primary
와 verified
가 true
인 email
을 찾는다.
그러면 이런 식으로 결과가 나온다. 이런 코드를 쓰고 싶었던 거다.
이걸 브라우저에서 하는게 좋을거다. 콘솔이 훨씬 인터렉티브 하니까 말이다.
이렇게 primary
와 verified
가 모두true
인 email
을 찾아 봤다.
현재 찾고 싶은걸 array
에서 찾는 방법이였다.
const email = emailData.find(
(email) => email.primary === true && email.verified === true
);
if (!email) {
return res.redirect("/login");
}
} else {
return res.redirect("/login");
}
};
아까 쓴 find()
를 쓰면 된다. 그리고 만일 email
이 없다면 user
한테 verified
된 email
이 없을 수도 있기때문이다.
다시 login
화면으로 redirect
한다.
나중에는 에러 notificaton
을 보여주면서 redirect
시켜 본다.
만일 코드가 도착하게 되면 primary
이면서 verified
인 email
이 있다는 뜻이다.
user
데이터 또한 받게 된다. 그래서 user
를 로그인 시킬수도 있고 아니면 계정을 생성 시킬수도 있다.
왜냐하면 email
이 없다는 뜻일 테니깐 말이다.
그리고 동일한 user email
은 갖고 있지만 한명은 일반 password
로 로그인 하고
다른 하나는 Github
로 로그인 하는 user
를 어떻게 다룰지 알아 볼거다.
email
과 password
로 계정을 생성한 user
가 Github
로 로그인하려고 하면 어떻게 할 것인지 생각해 보는거다.
그리고 똑같은 email
이 있다면 어떻게 할지도 말이다.
두개의 계정을 만들게 할 것인가? 아니면 그 계정들을 하나로 통합 할것인가?
user
에게 이미 해당 email
로 만든 계정이 있다고 에러를 보낼 것인가?
다음 파트에서 다뤄 보도록 한다.
현재는 만족스럽다. 왜냐하면 email
이 있으니 어떤 user
인지 알수 있다.
다음 파트에선 좀더 생각해봐야 할 필요가 있다.어떻게 중복된 email
을 처리할건지 말이다.