fetch가 필요한데 fetch는 서버엔 없고 브라우저에만 존재한다.
사람들은 node-fetch
라는 패키지를 만들었다.
https://www.npmjs.com/package/node-fetch
npm i node-fetch
설치해 보도록 한다.
현재 에러로 통해 자바스크립트와 NodeJS
가 다른 플랫폼이라는 걸 알수 있다.
userController.js
에서
import User from "../models/User";
import bcrypt from "bcrypt";
import fetch from "node-fetch";
그러면 이제 fetch
가 정의 된다. request
들을 해본다. node
로 가 보면 에러는 안 보인다.
새로고침을 하면 코드는 만료가 된것 같다. node
로 다시 가보면 에러가 뜬다.
어찌 되었든 작동은 한다. error: 'bad_verification_code',
이렇게 나온다. error_description: 'The code passed is incorrect or expired.',
확인해 보면 코드가 정확하지 않거나 만료되었을 수 있다. 라고 한다.
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 data = await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
});
const json = await data.json();
console.log(json);
res.send(JSON.stringify(json));
};
그리고 맨 마지막에 이렇게 추가를 해줘야 한다. 그래야 프론트엔드에서 볼수 있다.
테스트를 해본다. 코드와 함께 되돌아오고 코드는 백엔드로 가서 결과를 보수 있을거다.
// 20220422091738
// http://localhost:4000/users/github/finish?code=be7cb0a94beec5341141
{
"access_token": "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",
"token_type": "bearer",
"scope": "read:user,user:email"
}
이런 결과가 나온다. access_token
이 생겼다. token_type
은 bearer
이고
scope
은 read:user
,user:email
이다.
url
에 나온 코드를 이용해서 Github
백엔드에 request
를 보내니 access_token
이 생겼다.
이제 다음 단계로 access_token
을 갖고 API
에 접근한다.
이제 access_token
을 가지고 user
의 정보를 얻을수 있다.
다시 한번 되짚어 보면 Github
에 user
를 보내고 user
가 Github
에서
"yes"
라고 하면 Github
는 코드를 준다. 그 코드를 가지고 access_token
으로 바꿔준다.
그럼 access_token
으로 Github API
를 사용해 user
정보를 가져 온다.
이제 JSON
에 있는 access_token
을 가져오면 된다.
const json = await data.json();
if("access_token" in json){
// access api
}else{
return res.redirect("/login")
}
};
json
에 access_token
이 있는 경우 API
에 접근하도록 한다.
그게 아니라면 res.render("login")
을 할거다.
그러나 login
을 render
하는게 아니다. error
를 render
하는거다.
그래서 우선 redirect
를 써본다. redirect("/login")
이라고 한다.
만약 response
안에 access_token
이 없다면 login
으로 redirect
된다.
그러나 이렇게 하면 별로 좋지 않다. user
에게 notification
을 보여주고 싶다.
그건 나중에 해본다. 아직 user
에게 notification
을 못 보낸다.
Pug template
을 render
하는건 너무 불편하다.
그렇게 하면 URL
에서 user
가 template
을 보게 된다.
user
가 template
를 봐선 안된다.
이건 단지 어디론가 데려다주는 URL
일뿐이다.
http://localhost:4000/users/github/finish?code=be7cb0a94beec5341141
그래서 Controller
밖에선 어떤것도 render
하고 싶지 않다.
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,
};
이 모든 건 nitification
을 구현하면 가능하다.
notification
은 user
가 /login
으로 redirect
하게 해줄테고
user
는 /login
에서 에러 메세지를 보게 될거다.
현재 이곳에서 render("/login")
을 하면 너무 불편하다.
const json = await data.json();
if("access_token" in json){
// access api
}else{
return res.render("login", {pageTitel})
}
그리고 그 뒤에 pageTitle
같은 이런 것들을 쓰게 되면 코드가 너무 지저분해진다.
그래서 redirect("/login")
을 사용한다.
나중에 notificaton
을 보내면서 어떻게 redirect
를 하는지 알아본다.
일단은 먼저 테스트해본다. JSON
안에 access_token
이 없다면 어떻게 되냐면
웹사이트를 새로고침을 해보면 알수 있다. 왜냐하면 현재 코드는 이미 2번씩이나 사용했는데
이건 2번씩 사용하지 못한다. 그래서 새로고침을 하면 login
으로 redirect
된다.
왜냐하면 access_token
이 JSON
안에 없었기 때문이다.
JSON
이 이렇게 생겼다는거 기억이 난다.
access_token: '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',
token_type: 'bearer',
scope: 'read:user,user:email'
}
그런데 코드에서 이미 토큰이 사용되었다면 에러가 발생한다.
error: 'bad_verification_code',
error_description: 'The code passed is incorrect or expired.',
그렇기 때문에 지금은 다시 login URL
로 redirect
되는거고
나중에는 redirect
되고나서 user
에게 "로그인 할수 없습니다" 이런식으로 메시지를 보여주게 한다.
3단계는 GET URl을 통해서 인증을 위한 access_token을 보내준다.
Authorization: token OAUTH-TOKEN
GET https://api.github.com/user
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
을 JSON
으로부터 넣어줘서
const {access_token} = json;
const userRequest = await fetch("https://api.github.com/user",{
headers: {
Authorization: `token ${access_token}`,
},
)
"https://api.github.com/user"
URL
을 넣어주고 그리고 headers
에
authorization
을 보내야 한다. 여기에 token
이란 string
을 집어 넣어준다.
그리고 JSON
안에 있는 access_token
을 쓰면 된다.
JSON
에는 토큰이 있다는걸 기억해야 한다.
access_token: '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',
token_type: 'bearer',
scope: 'read:user,user:email'
}
그러니 access_token
을 fetch
안의 headers
로 보내준다.
그리고 awit
을 2번씩이나 쓸 필요가 없다. 모든 await
들을 괄호 안에 넣고
awati
를 한 다음에 .json()
을 해준다.
const data = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
그래서 이건 필요가 없어 졌다.
const json = await data.json();
await
를 또 다른 await
안에 넣어 주었다. 그리고 data
대신에 token
을 받게 된다.
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
tokenRequest
이라고 해준다.
만일 tokenRequest
안에 access_token
이 있다면 여기에도 똑같은걸 할거다.
if ("access_token" in json) {
const { access_token } = json;
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
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();
} else {
return res.redirect("/login");
}
};
보다시피 fetch
를 요청하고 있다. fetch
가 돌아오면 해당 fetch
의 JSON
을 받게 된다.
그런후에 console.log(userRequest);
를 해준다.
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");
}
};
조금 많이 복잡하긴 하다.
새로고침을 하고 Continue with Github
를 해주면 누르면 다시 돌아오고 ,
code
를 access_token
으로 바꾼 다음에 access_token
을 이용해 Github API
로 간다.
그러면 user
데이터를 볼수 있다.
NodeJS
으로 가보면 잘 작동한다. 정보를 받아오고 있다.
보다시피 홈페이지는 계속 로딩중이다. 왜냐하면 아무것도 return
하지 않아서 그렇다.
어쨌든 작동은 하고 있다. Github
프로필 정보를 가져온다.
이제 이걸 사용하면 된다. 하지만 문제가 있다. email
이 null
값이다.
무슨 말이냐면 email
이 없거나 private
이라는 뜻이다.
그래서 email
이 null
값일때를 대비해서 또 다른 request
를 만들어야 한다.
일단 작동은 하고 있다. Github user
의 정보를 가져오고 있다.
물론 아무것도 return
을 해주지 않아서 계속 로딩 중이다.
이제 request
를 끝낸다.
그리고 다시 새로고침으로 시작을 하면 URL
이 바뀌는 것조차 보질 못했다.
모든게 너무 빨리 돌아가고 있다.
다음 파트에서 user
의 email
을 가져와 본다.
왜냐하면 여기서 email
을 요청했는데 받지 못했다.
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",
};
일단 받은 code
를 access_token
으로 바꾸었다.
그 access_token
을 가지고 Github API
를 이용해서 user
의 정보를 가져올수 있다.
일단 웹사이트에는 이 정도만 해도 충분하긴 하다. 깃헙 아이디만 필요한거라면 말이다.
하지만 현재 상태에서는 email
이 필요하다.