Github Login #04

0_CyberLover_0·2022년 4월 22일
0

Node.JS #05

목록 보기
4/19

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_typebearer이고

scoperead:user,user:email이다.

url에 나온 코드를 이용해서 Github백엔드에 request를 보내니 access_token이 생겼다.

이제 다음 단계로 access_token을 갖고 API에 접근한다.

이제 access_token을 가지고 user의 정보를 얻을수 있다.

다시 한번 되짚어 보면 Githubuser를 보내고 userGithub에서

"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")
  }
};

jsonaccess_token이 있는 경우 API에 접근하도록 한다.

그게 아니라면 res.render("login")을 할거다.

그러나 loginrender하는게 아니다. errorrender하는거다.

그래서 우선 redirect를 써본다. redirect("/login")이라고 한다.

만약 response안에 access_token이 없다면 login으로 redirect된다.

그러나 이렇게 하면 별로 좋지 않다. user에게 notification을 보여주고 싶다.

그건 나중에 해본다. 아직 user에게 notification을 못 보낸다.

Pug templaterender하는건 너무 불편하다.

그렇게 하면 URL에서 usertemplate을 보게 된다.

usertemplate를 봐선 안된다.

이건 단지 어디론가 데려다주는 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을 구현하면 가능하다.

notificationuser/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_tokenJSON안에 없었기 때문이다.

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 URLredirect되는거고

나중에는 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_tokenJSON으로부터 넣어줘서

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_tokenfetch안의 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가 돌아오면 해당 fetchJSON을 받게 된다.

그런후에 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를 해주면 누르면 다시 돌아오고 ,

codeaccess_token으로 바꾼 다음에 access_token을 이용해 Github API로 간다.

그러면 user데이터를 볼수 있다.

NodeJS으로 가보면 잘 작동한다. 정보를 받아오고 있다.

보다시피 홈페이지는 계속 로딩중이다. 왜냐하면 아무것도 return하지 않아서 그렇다.

어쨌든 작동은 하고 있다. Github 프로필 정보를 가져온다.

이제 이걸 사용하면 된다. 하지만 문제가 있다. emailnull값이다.

무슨 말이냐면 email이 없거나 private이라는 뜻이다.

그래서 emailnull값일때를 대비해서 또 다른 request를 만들어야 한다.

일단 작동은 하고 있다. Github user의 정보를 가져오고 있다.

물론 아무것도 return을 해주지 않아서 계속 로딩 중이다.

이제 request를 끝낸다.

그리고 다시 새로고침으로 시작을 하면 URL이 바뀌는 것조차 보질 못했다.

모든게 너무 빨리 돌아가고 있다.

다음 파트에서 useremail을 가져와 본다.

왜냐하면 여기서 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",
  };

일단 받은 codeaccess_token으로 바꾸었다.

access_token을 가지고 Github API를 이용해서 user의 정보를 가져올수 있다.

일단 웹사이트에는 이 정도만 해도 충분하긴 하다. 깃헙 아이디만 필요한거라면 말이다.

하지만 현재 상태에서는 email이 필요하다.

profile
꿈꾸는 개발자

0개의 댓글