들어가기
github로 로그인을 해 본다
flow가 거의 같기 때문에
이걸 익히면 kakao login, facebook login등을
쉽게 할 수 있음.
https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps
공식문서.
Flow
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을 참조할것
-어떻게 이뤄지는것지는 이해해야함.
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
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()
}
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 →