지난 시간에는 local에서 로그인을 구현했다. 이번에는 github로 구현해보자.
export const githubLogin = (req, res) => {
const base_url = "https://github.com/login/oauth/authorize";
const config = {
client_id: process.env.CLIENT_ID,
allow_signup: false,
scope: "read:user user:email", // space로 구분한다.
};
const config_url = new URLSearchParams(config).toString();
const final_url = `${base_url}?${config_url}`;
return res.redirect(final_url);
};
client_id : new OAuth app을 만들면 생기는 client ID
scope : github에게 요청하는 유저 정보 종류들
URLSearchParams : url 관련 object이다. toString()을 사용하면 string을 url로 바꿔준다.
성공적으로 github로부터 응답을 받으면 code를 받게 된다. 이 code는 10분 동안만 유효하며 한 번 사용하면 재사용이 불가능하다.
export const githubCallback = async (req, res) => {
const base_url = "https://github.com/login/oauth/access_token";
const config = {
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
code: req.query.code,
};
const config_url = new URLSearchParams(config).toString();
const final_url = `${base_url}?${config_url}`;
const tokenRequest = await (
await fetch(final_url, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
};
✔ json 형태로 발급받는 경우
const data = await fetch(final_url, {
method: "POST",
headers: {
Accept: "application/json",
},
})
const json = await data.json();
console.log(json);
▶ 출력값
{
access_token: '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',
token_type: 'bearer',
scope: 'read:user,user:email'
}
✔ text 형태로 발급받는 경우
const data = await fetch(final_url, {
method: "POST",
headers: {
Accept: "text/html",
},
})
const text = await data.text();
console.log(text);
▶ 출력값
access_token=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&scope=read%3Auser%2Cuser%3Aemail&token_type=bearer
access_token을 발급받으면 유저의 정보를 열람할 수 있다. 단, scope에서 요청한 정보만 볼 수 있다.
export const githubCallback = async (req, res) => {
...
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);
return res.redirect("/");
} else {
return res.redirect("/login");
}
};
▶ 출력값
{
...
type: 'User',
site_admin: false,
name: 'abc',
company: null,
blog: '',
location: null,
email: 'abc@naver.com',
hireable: null,
...
}
이로써, github로 로그인을 해보았다.
유저가 email을 private으로 설정하면 어떻게 될까? email: null
이 출력된다. 이런 경우에는 어떻게 해야 할까?
github에서 관리하는 Users를 참고해보자.
export const githubCallback = async (req, res) => {
...
if ("access_token" in tokenRequest) {
...
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(emailData)
const email = emailData.find(
(email) => email.primary === true && email.verified === true
);
return res.redirect("/");
} else {
return res.redirect("/login");
}
};
▶ 출력값
[
{
email: 'abc@naver.com',
primary: true,
verified: true,
visibility: 'public'
},
{
email: '80094949+abc@users.noreply.github.com',
primary: false,
verified: true,
visibility: null
}
]
github가 가지고 있는 모든 email이 출력됐다. primary: true, verified: true
인 email을 찾으면 된다.
앞서 fetch를 사용할 때 headers를 사용했다. headers에 대해 알아보자.
headers는 브라우저와 서버가 요청 또는 응답할 때, 정보를 전송할 수 있도록 해준다. 여기서 정보의 형태를 결정하는 application/json
, text/html
등을 "MIME type"이라고 부른다.