github Login

김종민·2022년 11월 1일
0

Youtube

목록 보기
15/23

들어가기
github로 로그인을 해 본다
flow가 거의 같기 때문에
이걸 익히면 kakao login, facebook login등을
쉽게 할 수 있음.


https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps

공식문서.

Flow

  1. Users are redirected to request their GitHub identity
  2. Users are redirected back to your site by GitHub
  3. Your app accesses the API with the user's access token

github.com/settings/applications


1. 위 페이지에 접속해서 Developer settings를 클릭한다


2. OAuth Apps를 클릭하고, 오른쪽의 New Oauth App을 클릭한다.


3. Application name입력, Hompage URL입력,
-description 입력
-Authorization callback URL은 일단 아무데나 입력하고,
나중에 알게됨(callback부분은 finishs로 바꿔준다..매우중요)
-이 path는 userRouter에서 '/github/finish로
-path 및 controler를 만들어 준다.
-Resister application 클릭


이런 화면을 보게 됨.

a(href='https://github.com/login/oauth/authorize?client_id=92bcfd75e0bd155308f8
위의 주소에 client_id는 위에서 확인한 client_id를 넣어서
클릭하면 화면이 하나 나옴.

&allow_signup=false&scope=read:user user:email') Continue with GitHub → (client_id)뒤에 붙을 부분

-allow_signup은 github account가 없으면 가입할것인지 물어보는것
-false로 하면 안물어보는것.
-scope는 github에 있는 User의 정보를 얼마나 가져올것인지를 설정
-위에서는 user랑 user의 email을 가져옴,
-user만 가져왔을떄는 email이 안따라옴(공식문서 참조!!)

-이렇게 하면 url이 너무 길어져서 아래 userController을 참조할것
-어떻게 이뤄지는것지는 이해해야함.

1. userRouter.js

import express from 'express'
import {
  finishGithubLogin,
  getChangePassword,
  getUserEdit,
  logout,
  postChangePassword,
  postUserEdit,
  seeUser,
  startGithubLogin,
} from '../controllers/userController'
import {
  avatarUpload,
  protectorMiddleware,
  publicOnlyMiddleware,
} from '../middlewares'

const userRouter = express.Router()

userRouter.get('/:id([0-9a-f]{24})', seeUser)
userRouter
  .route('/edit')
  .get(getUserEdit)
  .all(protectorMiddleware)
  .post(avatarUpload.single('avatar'), postUserEdit)
userRouter.get('/logout', logout)

userRouter.get('/github/start', publicOnlyMiddleware, startGithubLogin)
///첫번쨰로 start path를 만든다.
userRouter.get('/github/finish', publicOnlyMiddleware, finishGithubLogin)
///두번째로 finish path를 만든다.

userRouter
  .route('/change-password')
  .all(protectorMiddleware)
  .get(getChangePassword)
  .post(postChangePassword)

export default userRouter

2. userController.js

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}`
  res.redirect(finalUrl)
  //- a(href='https://github.com/login/oauth/authorize?client_id=92bcfd75e0bd155308f8&allow_signup=false&scope=read:user user:email') Continue with GitHub →
}
///위에서 만든 url을 여기서 다시 만들어줌.
///option부분은 config에 다 넣어줌. client_id는 .env로
///위와 같이 sexy Url을 만들때, params에서 보듯이
///new URLSearchParams랑 toString()을 해 주어야
///web이 이해할 수 있는 Url이 만들어짐.
///최종 finalUrl을 만들어 return해 줌.
///위와 같이 req를 보내면 맨 위에서 git site의
///Ahthorization callback URL(localhost:4000/users/github/callback)
///여기 site로 user을 정보를 github에서 나의 web으로 보내줌.
///url에 code를 포함시켜서 보내줌.(code를 이용해 user정보 받음)
///localhost:4000/users/github/callback?code=ewrf4w3wweg
///위와 같은 방식으로 보내줌,
///매우중요!!! callback부분을 finish로 바꿔준다.

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,
    ///client_id를 확인하는 page에서 Generate a new client
    ///secret 버튼을 확인하고, 클릭하면, 볼 수 있음.
    code: req.query.code, ///coed는 req.query로 받음.
    ///'/github/finins?code=FDFGDFGwewefsdf'의
    ///code부분은 따내옴.
  }
  const params = new URLSearchParams(config).toString()
  ///브라우저가 이애할 수 있는 Url로 바꾸어주는 작업.
  /// new URLSearchParams와 toString()를 이용해서
  
  const finalUrl = `${baseUrl}?${params}`
  
  const tokenRequest = await (
    await fetch(finalUrl, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
      },
    })
  ).json()
  ///id, secret, code등을 finalUrl에 넣어서 POST로
  ///finalUrl로 fetch하면, access_token을 받을 수 있음.
  ///header부분 주의할 것!!
  ///fetch는 backend에서 fetch를 쓸려면, 
  ///npm i node-fetch를 깔아줘야 된다.(매우중요!!!!!)
  ///import fetch from 'node-fetch'

  // console.log(json)
  // res.send(JSON.stringify(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()
    ///위에서 access_token을 확인했으면, apiUrl/user에
    ///headers에 Authorization에 access_token을 담아
    ///fetch하면, userData를 받을 수 있다.
    ///await가 두번 들어간것은, 안의 fetch에서 먼저 fetch를 하고,
    ///두번째 await로 fetch한 data를 json()화 하기 위함임.
    console.log(userData)
    
    const emailData = await (
      await fetch(`${apiUrl}/user/emails`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json()
    ///위와 마찬가지로 apiUrl에 user/email로
    ///headers에 token을 담아서 fetch하면,
    ///user의 emailData를 받을 수 있음.
    ///data를 json()화 하기 위해서 await 두번하는거
    ///유의할 것!!!
    
    console.log(emailData)
    ///2개의 email Data를 확인할 수 있는데, 2개중에
    ///email이 verified이면서 primary가 true인것을 담아야됨.
    
    const emailObj = emailData.find(
      (email) => email.primary === true && email.verified === true
    )
    ///2개의 email중에서 verified와 primary가 true인 것을
    ///emailObj에 담는다.
    
    console.log('email', emailObj)
    if (!emailObj) {
      return res.redirect('/login')
    }
    let user = await User.findOne({ email: emailObj.email })
    ///let으로 git으로 받아온 userData의email과 DB에 있는
    ///user의 email과 같은 email을 찾는다.

	///만약 같은 email의 유저가 없다면, git에서 받아온
    ///Data로 user를 만든다. 
    ///soicalOnly를 true로 하는 이유는 git으로 create된
    ///user는 socialOnly를 true로 해서 password를 바워둔다.
    if (!user) {
      user = await User.create({
        avatarUrl: userData.avatar_url,
        name: userData.name,
        username: userData.login,
        email: emailObj.email,
        location: userData.location,
        password: '',
        socialOnly: true,
      })
    }
    ///위에서 만든 User의 정보를 session에 담아준다.
    ///그리고 홈으로 redirect한다.
    req.session.loggedIn = true
    req.session.user = user
    return res.redirect('/')
  } else {
    res.redirect('/login')
  }
  res.end()
}

3. login.pug

extends base.pug

block content 
    if errorMessage 
        span=errorMessage
    form(method="POST")
        input(name="username", placeholder="Username", type="text", required)
        input(name="password", placeholder="Password", type="password", required)
        input( type="submit", value="Login")
        br
        a(href='/users/github/start') Continue with GitHub →
///login page에 github Login하는 button을 하나 만든다.
        
        //- a(href='https://github.com/login/oauth/authorize?client_id=92bcfd75e0bd155308f8&allow_signup=false&scope=read:user user:email') Continue with GitHub →
    hr
    div 
        span Already have an account?
        a(href='/join') _ Create account Now →
profile
코딩하는초딩쌤

0개의 댓글