Recap

0_CyberLover_0·2022년 4월 24일
0

Node.JS #05

목록 보기
8/19

이전까지 한 모든 것들은 Github로 로그인을 하기 위함이었다.

링크를 누르면 온갖 기능들이 실행된다.

http://localhost:4000/users/github/start

알다시피 이링크는 users/github/start로 가고 있다.

그건 현재 만든 userRouter.js에 생성한 route이다.

routecontroller를 가지고 있다.

그리고 이 controller의 유일한 역할은 몇몇 configuration parameter

가지고 URL을 만드는 거다.

그래서 baseUrlparameter들을 연결 시켰다.

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",
  };
  const params = new URLSearchParams(config).toString();
  const finalUrl = `${baseUrl}?${params}`;
  return res.redirect(finalUrl);
};

그렇게 해서 finalUrl을 만들고 여기로 user를 보내주었다.

userGithub을 보낸거다.

URL을 설정하는 이유는 URLGithub에게 뭔가를 알려줄수 있어서다.

예를 들면 client_id같은거다. 그래야 Github이 어떤 어플에 로그인하는지 알수 있다.

회원가입도 마찬가지이다.

예를 들어 어플에 어떤 종류의 user를 허용 시킬건지 설정 할수 있다.

그리고 scope에는 user로 뭘 할건지 설정하면 된다.

원하는 어떤 scope도 사용 할수 있다. 하지만 해당 scope으로 요청해야한다.

페이스북이나 구글 같은 웹사이트는 아주 큰 scope을 요구할때 어플 검증을 받아야 한다.

그게 며칠씩이나 걸릴수 있기에 귀찮다.

반면 Github로는 프로필을 읽고 user email에 접근 하는 것외엔 없다.

그래서 Githubuser를 보낼거다.

Github는 비밀번호, 이메일, 보안 관련된 모든 데이터를 가지고 있는데

이 데이터들을 공유하는데 동의를 하면 웹사이트로 되돌아 올거다.

Github/github/finish라는 URL로 돌려 보내준다.

userRouter.get("/github/finish", finishGithubLogin);

좀 정확히 말하자면 localhost:4000/users/github/finish이다.

URL을 어디에도 만들지 않았다. Github.com 웹사이트에서 만들었다.

URLfinishGithubLogin이라는 function으로 호출 하는거다.

function은 굉장히 크다. 무슨 내용 들이 있는지 살펴 보면

먼저 userGithub에서 돌아오면 이런 URL?code=xxx가 덧붙여진 내용을 받는다.

/gihut/finish?code=XXX이런 식이다.

codeuser가 승인했다고 Github가 알려주는 거다.

그리고 나서 baseUrl과 몇몇 parameter들을 받고

parameter들을 URLparameter string으로 바꿔준다.

const params = new URLSearchParams(config).toString();
  const finalUrl = `${baseUrl}?${params}`;
  const tokenRequest = await (
    await fetch(finalUrl, {
      method: "POST",
      headers: {
        Accept: "application/json",
      },
    })
  ).json();

그리고 다른 URL을 만든다.

baseUrlconfig를 더해서 다른 URL을 만드는 거다.

그리고 그 URL로 무엇을 하냐면 POST request를 보낸다.

URL에는 Github가 준 code가 담겨져 있다.

모든 것이 올바르다면 Githubaccess_token을 준다.

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();

access_tokenGithub API와 상호작용 할때 쓸거다.

Github API는 굉장히 크다 Github API에 어떤 method를 보내더라도 응답을 보내준다.

access_token만 있다면 말이다. 그리고 user프로필을 받기 위해 요청할수 있다.

그 요청은 api.github.com/user로 갈것이고

access_token을 보내면 user데이터를 받을수 있을거다.

이걸 짧게 쓸수 있는데 await fetchawait json이 그거다.

(이전 파트에서 await를 쓰지 않으면 어떻게 해야 하는지 해 보았다.)

가끔은 user들이 email을 보여주지 않을때도 있다.

그렇기 때문에 email API에게도 요청을 보내줘야 한다.

const emailData = await (
      await fetch(`${apiUrl}/user/emails`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json();

그래서 여기에도 똑같은 access_token으로 요청을 보내주면 email arrary를 준다.

그리고 그 email arrary에서 primary이면서 verifiedemail을 찾을거다.

const emailObj = emailData.find(
      (email) => email.primary === true && email.verified === true
    );
    if (!emailObj) {
      return res.redirect("/login");
    }
    let user = await User.findOne({ email: emailObj.email });
    if (!user) {
      user = await User.create({
        avatarUrl: userData.avatar_url,
        name: userData.name ? userData.name : userData.login,
        username: userData.login,
        email: emailObj.email,
        password: "",
        socialOnly: true,
        location: userData.location,
      });
    }
    req.session.loggedIn = true;
    req.session.user = user;
    return res.redirect("/");
  } else {
    return res.redirect("/login");
  }
};

만일 찾지 못한다면 user를 로그인 페이지로 돌아가게 만들고 나중에는 notification을 설정해준다.

(그건 나중에 해보도록 한다.)

notification을 설정하는 이유는 유저한테 Github로 로그인 했다는걸 알려주기 위해서다.

그런데 Githubverificationemail이 없으니 믿을수 없는거다.

만일 primary이면서 verifiedemail을 찾게 된다면

데이터베이스에서 해당 email을 찾게 된다면 유저를 로그인 시킬거다.

만일 데이터베이스에서 email을 찾지 못했을 경우 그 emailuser를 만들거다.

해당 emailGithub에 보낸 모든 데이터를 가지고 user를 만든다.

socialOnlytrue이면 Github로 로그인을 통해 만들어진 계정이란 뜻이 된다.

그렇기에 해당 사용자는 password가 없으므로 login form을 사용할수 없다.

원한다면 무작위 password를 만들어 볼수도 있다.

Github가 무작위 password를 줄거라고 생각한다.

node_id가 바로 무작위 password이다.

 node_id: 'U_kgDOBXfSMQ',

원한다면 유저가 비밀번호를 아예 모르게끔 설정 할수 있다. 빈 passwordsocialOnlytrue로 만들었다.

그 이유는 postLogin에서

export const postLogin = async (req, res) => {
  const { username, password } = req.body;
  const pageTitle = "Login";
  const user = await User.findOne({ username, socialOnly: false });
  if (!user) {
    return res.status(400).render("login", {
      pageTitle,
      errorMessage: "An account with this username does not exists.",
    });
  }

user가 로그인 하는걸 체크할때 socialOnlyfalse인걸 확인하려는 거다.

왜냐하면 socialOnlyfalse이면 usernamepassword로만 로그인 할수 있는 유저이고

socialOnlytrue라는건 password가 없다는 소리이다.

중요한 건 여기에서 username email 같은 걸 전부 쓰고 있기 때문에 뭔가 하나로 통합하고 싶을거다.

그러면 username필드를 제거해 본다.

usernameemail이 공존할 필요 없이 email만 써도 된다.

다음 포인트는 이것들이 전부 실행되면 쿠키가 생긴다는 거다.

 if (!user) {
      user = await User.create({
        avatarUrl: userData.avatar_url,
        name: userData.name ? userData.name : userData.login,
        username: userData.login,
        email: emailObj.email,
        password: "",
        socialOnly: true,
        location: userData.location,
      });
    }

logout기능도 구현했다. 정말 단순하다.

export const logout = (req, res) => {
  req.session.destroy();
  return res.redirect("/");
};

그리고 logoutControllerlogout URL을 만들었다.

userRouter.get("/logout", logout);

controlleruserController로부터 온다. sessiondestroy했고 그 다음 redirect를 했다.

userredirect 되면 session이 없어진다. 그러면 유저가 로그아웃 된다.

이런 흐름은 어디에서든 적용이 된다. 물론 페이스북이나 구글 같은 곳은 좀 더 복잡할거다.

왜냐하면 거긴 더 크니 인증 절차 거치고, 기다리고 그러기 때문이다.

그런데 트위터, 카카오톡, 인스타그램은 매우 비슷하다. 물론 client_id같은 이름이 똑같지 않을 거다.

app_id 일수도 있다. 하지만 방법은 똑같은 거다.

user를 소셜 웹사이트로 보내면 몇몇 code와 함께 웹사이트로 돌려보내고

codeAPIrequest를 보낼수 있다. 이제 user인증을 끝냈다.

다음 파트에선 user프로필 부분을 할거다. 그리고 파일에 대해서다 알아 보겠다.

왜냐하면 Github으로 로그인해서 계정을 만들때 user에게 avatar URL을 준다.

그 말인 즉슨 user한테 avatarUrl이 있다는 거다.

그런데 소셜 로그인이 아닌 다른 방법으로 계정을 만든 경우를 생각해보면

emailpassword로 계정은 만든 유저들은 avatarUrl이 없다.

그래서 프로필 수정을 구현하고 백엔드에 어떻게 파일들을 보낼수 있는지 알아 보겠다.

Github user뿐만아니라 모든 useravatarUrl을 가지게 할거다.

비디오를 만들때도 써 먹을수 있을거다. 비디오 파일을 보낼수 있어야 하기 때문이다.

그게 한 가지 기대되는 포인트고 또 다른 하나는 프로필 수정 페이지를 만들어 볼거다.

프로필 수정 페이지에서는 avatar를 설정할수 있고 원하는 모든 걸 수정 할수 있게 만들거다.

password도 바꿀 수 있게 한다.

하지만 socialOnlytrue인 경우는 제외 할거다.

socialOnlytrue이면 password를 가지고 있지 않으니깐 말이다.

profile
꿈꾸는 개발자

0개의 댓글