계속해서 GitHub 로그인 구현을 해보록 한다
이번 파트에서 살펴 볼것은 scope
이다.
scope
은 유저에게서 얼마나 많이 정보를 읽어내고 어떤 정보를 가져올 것에 대한거다.
그러니 당연히 필요한 정보만 요청 하도록 해야 한다.
https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps
예를 들자면 여기 나와 있는 모든 scope
을 보면 여기에서 admin scope
을 가져 올수 있다.
무슨 말이냐면 organization
을 읽고 쓴다거나 public key
에 접근 가능하다.
public key
통째로 관리 할수 있고 public key
를 쓸수 있고
public key
를 읽을 수 있는 등 아주 다양하다.
혹은 모든 notification
에 대해 watch/unwatch
에 대한 접근이 가능하다.
user
도 마찬가지이다. user
의 프로필로 부터 읽고 쓸수도 있다.
뭘 원하는지에 따라 달려있다. write
할 필요가 없다.
그냥 read
만 하면 된다. package
를 읽을 필요도 없고 gpg_key
도 읽을 필요도 없다.
원하는건 user
을 읽을 수 있어야 하고 user
의 email
을 가져오는 거다.
원하는건 그게 다이다. read:user
랑 user:email
이다.
그럼 바로 적용해 보도록 한다.
여기 보면 굉장히 긴 URL
이 보인다.
block content
if errorMessage
span=errorMessage
form(method="POST")
input(placeholder="Username",name="username", type="text", requeired)
input(placeholder="Password",name="password", type="password", requeired)
input(type="submit", value="Login")
br
a(href="https://github.com/login/oauth/authorize?client_id=2c44ee26963db98266d4&allow_signup=false") Continue with GitHub →
그러니 뭔가 하나를 더 추가해 본다.
이 permission
은 scope
이라고 부르기도 하는데 여기에 공백으로 구분되는 scope
목록을 보내준다.
그럼 예시로 user:email
부터 해본다.
block content
if errorMessage
span=errorMessage
form(method="POST")
input(placeholder="Username",name="username", type="text", requeired)
input(placeholder="Password",name="password", type="password", requeired)
input(type="submit", value="Login")
br
a(href="https://github.com/login/oauth/authorize?client_id=2c44ee26963db98266d4&allow_signup=false&scope=user:email") Continue with GitHub →
잘 작동 하는지 확인해 보기로 한다. 새로고침하고 Continue with GitHub
클릭해보면
전이랑은 다르게 보인다. 이제 Personal user data
라고 명시되어 있다.
아니면 url
에서 user
라고 쳐본다.
그러면 이제 모든 Personal data
에 접근 가능하다.
읽고 쓰는 등 모든 접근이 가능하다는 거다. 이렇게 까지는 필요 하지 않다.
결국 이 scope parameter
에 따라 달라진다는 거다.
이 말인 즉슨 GitHub
랑 대화 할때 "난 이런이런 정보를 원해"라고 요구하는것과 같다.
어떤 정보를 요청하느냐에 따라 user
는 승인을 받게 되고 GitHub
는 정보에 접근 할수 있도록 token
을 줄거다.
보다시피 전부 URL
에 기반하고 있다.
카카오톡 로그인을 구현할 때에도 마찬가지로 똑같이 해줘야 한다.
카카오톡이 주는 URL
을 받아내고 카카오톡이 주는 ID
를 집어 넣고 scope
을 집어 넣게 된다.
요청하고 싶은 것이 담긴 API
문서가 있을거다. read:user
라는 걸 한 번 보도록 한다.
여기 read:user
을 집어 넣어 준다.
그러면 적용 된다. Personal user data
라고 나온다.
하나 더 추가해본다. 공백으로 구분하면 된다. user:email
을 복사하고
여기에다가 공백과 user:email
을 넣어 준다.
이렇게 email
주소와 프로필 정보를 원한다고 나온다.
Email addresses (read-only), profile information (read-only)
더 많은 걸 요청 할수도 있다. 그리고 이것들은 전부 URL
에 공백으로 구분해서 쓰면 된다.
다른 것도 한번 해본다. 이번에는 delete_repo
넣어 본다.
공백을 넣어주고 delete_repo
라고 넣어준다. 보다시피 delete repo
가 추가 되었다.
그러면 유저가 "예/아니오"를 할수 있다.
여기에 " 이 어플리케이션은 어떤 repository
든 삭제 할수 있다" 라고 나온다.
This application will be able to delete any repository to which you have admin rights.
이건 별로 좋아 보이진 않다. 예전에도 봤겠지만 이 URL
상에 요청하는 모든 것들이 다 들어 있다.
한가지 문제점은 이 긴 URL
을 계속해서 변경 해줘야 한다는 거다. 보다시피 너무 길다.
그리고 GitHub
로 계속 되는 똑같은 URL
을 원한다고 가정 하면
이걸 Join
에 넣는다고 하면 이걸 전부 복붙해야 된다.
scope
을 하나 바꾼다 해도 여기 저기 또 바꿔줘야 하고 클라이언트 ID
가 바뀌면 다른 template
에도 바꿔줘야한다.
그래서 한 군데에 전부 모아두는게 좋다. 물론 이걸 이렇게 쓰게 되면 굉장히 복잡하고 혼란스럽게 된다.
이렇게 바꿔준다.
block content
if errorMessage
span=errorMessage
form(method="POST")
input(placeholder="Username",name="username", type="text", requeired)
input(placeholder="Password",name="password", type="password", requeired)
input(type="submit", value="Login")
br
a(href="/users/github/start") Continue with GitHub →
"/users/github/start"
이렇게 해준다. 그리고 아직 route
가 없으니 새로운 route
를 만든다.
user.Router.js
에서 /users
는 이미 되어 있으니 상관없고 /github
를 추가한다.
userRouter.get("/logout", logout);
userRouter.get("/edit", edit);
userRouter.get("/remove", remove);
userRouter.get("/github/start", startGithubLogin);
userRouter.get(":id", see);
그리고 이건 startGithubLogin
이라는 controller
를 사용한다.
그런데 아직 이 controller
를 만들지 않았으니 만들어 준다.
userController.js
에서
export const startGithubLogin = (req, res) => {}
export const edit = (req, res) => res.send("Edit User");
export const remove = (req, res) => res.send("Remove User");
export const logout = (req, res) => res.send("Log out");
export const see = (req, res) => res.send("See User");
그리고 이건 userController
의 이 위치에 놓을 거다.
그러면 이제 userController
를 import
할 수 있다.
userRouter.js
에서
import express from "express";
import {
edit,
remove,
logout,
see,
startGithubLogin,
} from "../controllers/userController";
const userRouter = express.Router();
userRouter.get("/logout", logout);
userRouter.get("/edit", edit);
userRouter.get("/remove", remove);
userRouter.get("/github/start", startGithubLogin);
userRouter.get(":id", see);
export default userRouter;
import
를 해주었다. 이제 URL
이 생겼다. 이 URL
은 user
를 Github
으로
redirect
를 시킬건데 이 URL
을 좀더 이쁘게 만들어 본다.
user.controller.js
에서
export const startGithubLogin = (req, res) => {
const baseUrl = "https://github.com/login/oauth/authorize";
여기 startGithubLogin
으로 와서 뭘 할거냐면 이 URL
전부 말고 authorize
까지만 넣어준다.
그리고 const clienID
를 넣어준다.
export const startGithubLogin = (req, res) => {
const clientId = "2c44ee26963db98266d4";
const baseUrl = "https://github.com/login/oauth/authorize";
이제 이것들은 합쳐져 하나의 URL
이 될거다.
하지만 그전에 login.pug
에서
block content
if errorMessage
span=errorMessage
form(method="POST")
input(placeholder="Username",name="username", type="text", requeired)
input(placeholder="Password",name="password", type="password", requeired)
input(type="submit", value="Login")
br
a(href="/users/github/start") Continue with GitHub →
URL
을 이렇게 작성해준다.
다음으로 allow_signup = false
와 user:email
scope
이 필요하다.
export const startGithubLogin = (req, res) => {
const config = {
clientId :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
}
const baseUrl = "https://github.com/login/oauth/authorize";
};
그래서 config
객체를 만들어 준다. clientId
를 이 객체 안에다가 넣어준다.
allow_signup = false
로 해주고 scope
은 몇가지 scope
들을 보내준다.
어떤 scope
을 사용할거냐면 read:user
와 user:email
이다.
꼭 공백으로 나눠 줘야 한다.
이게 configuration
객체이다. 이제 URL
로 바꿔 준다.
아니면 이걸 인코딩해서 URL
상에서도 동작 할수 있게 할수도 있다.
그래서 UrlSearchParams
라 하는 utility
를 써볼거다.
export const startGithubLogin = (req, res) => {
const config = {
clientId :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
URLSearchParams
const baseUrl = "https://github.com/login/oauth/authorize";
};
브라우저에서 테스트 해 볼수도 있다. 테스트 해 본다. inspect
(검사)로 들어가서
console
에서 이 config
객체를 복붙해본다.
const config = {
clientId :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
undefined
new URLSearchParams(config)
URLSearchParams {}[[Prototype]]: URLSearchParams
new URLSearchParams(config).toString()
'clientId=2c44ee26963db98266d4&allow_signup=false&scope=read%3Auser+user%3Aemail'
new URLSearchParams(config)
을 하면 물론 아무것도 생기지 않는다.
그렇지만 new URLSearchParams(config).toString()
으로 연결 가능하다.
그러면 이런 것들을 얻지만 인코딩 되어서 나온다.
이제 다시 돌아가서
export const startGithubLogin = (req, res) => {
const config = {
clientId :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
const params = new URLSearchParams(config);
const baseUrl = "https://github.com/login/oauth/authorize";
};
여기서부턴 뭔가가 작동되지 않을거다. 왜냐하면 clientId variable
을 만들어 줬지만
이건 Github
가 기다리고 있는것과는 다르다.
Github
는 글자 그대로 client_id
를 기다리고 있는거다.
그래서 아무리 javascript
상에 있다 하더라도 Github
가 원하는 parameter
로 전송 해줘야 한다.
export const startGithubLogin = (req, res) => {
const config = {
client_id :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
const params = new URLSearchParams(config);
const baseUrl = "https://github.com/login/oauth/authorize";
};
config
도 있다. parameter
도 만들었다. base URl
도 있다.
이제 뭘 하냐면 여기서 봤듯이
const config = {
clientId :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
undefined
new URLSearchParams(config)
URLSearchParams {}[[Prototype]]: URLSearchParams
new URLSearchParams(config).toString()
'clientId=2c44ee26963db98266d4&allow_signup=false&scope=read%3Auser+user%3Aemail'
저것들을 전부 URL
에 넣어줘야 한다. 다시 돌아가서
export const startGithubLogin = (req, res) => {
const config = {
client_id :"2c44ee26963db98266d4",
allow_signup : false,
scope: "read:user user:email",
};
const params = new URLSearchParams(config).toString();
const baseUrl = `https://github.com/login/oauth/authorize?${params}`;
return res.direct(baseUrl);
};
toString()
을 쓰고 아래에는 ?
를 추가 한다. 그리고 이렇게 query
안에 parameter
을 넣어준다.
그리고 ${params}
라 적어 준다. 이렇게 URL
이 완성 되었다.
하지만 그전에 return res.direct()
해주고 baseUrl
로 가게 해준다.
사실 baseURL
로 가면 안된다. baseURl
은 ${params}
이 부분이니깐
export const startGithubLogin = (req, res) => {
const baseUrl = "https://github.com/login/oauth/authorize";
const config = {
client_id: "2c44ee26963db98266d4",
allow_signup: false,
scope: "read:user user:email",
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
return res.redirect(finalUrl);
};
이렇게 수정해줘야지 맞게 된다.finalUrl
이라고 해준다. 그럼 이제 user
는 finalUrl
로 가게 된다.
그리고 길고 긴 string
을 가지는 것보다 이렇게 적는 편이 훨씬 좋다.
이제 어디서든 이 URl
을 user
에게 보낼수 있게 되었고 작동도 잘 된다.
한줄로 길게 link
를 만들 필요가 없다. 테스트 해본다.
새로고침하고 Login
해주고 Continue with GitHub
클릭해주면 잘 작동 한다.
즉 이 URL
을 건드려서 URL
을 만든후
userRouter.get("/github/start", startGithubLogin);
export const startGithubLogin = (req, res) => {
const baseUrl = "https://github.com/login/oauth/authorize";
const config = {
client_id: "2c44ee26963db98266d4",
allow_signup: false,
scope: "read:user user:email",
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
return res.redirect(finalUrl);
};
여기로 다시 redirect
된다. 확인해 보면 Authorization
을 요구하고 있고
email
주소와 프로필 정보를 읽고 싶다고 요청했다.
한 가지 더 해야 할건 Authorize
를 클릭 할수 있게 만드는 거다.
그리고 Authorize
를 클릭할 때 URL
에 무슨 일이 일어나는지 잘 보도록 한다.
클릭하고 나면
http://localhost:4000/users/github/callback?code=a8619d69b2f8e2dbee8c
으로 다시 redirect
시켜준다. 이 URL
을 최근에 본게 어디였는지 생각을 해보니
Authorization callback URL
처음 설정 할때 지정한 URl
이다.
만일 Github
의 user
가 "yes"
라고 답한다면 Github
는 user
를 어플리케이션의
http://localhost:4000/users/github/callback
이 URL
로 보내주게 된다. 그리고 말했다시피 user
에게 code
를 보내주게 된다.
code=a8619d69b2f8e2dbee8c
이 코드를 말이다.이 코드는 나중에 사용할거다.
다음 파트에서 다뤄 보기로 한다.
왜냐하면 현재 가지고 있는 않는 이 URL
과 contorller
를 만들어줘야 한다.
다행히도 이미 /github/start
를 가지고 있다. 하지만 /github/callback
이 필요하다.
아니면 이걸 /finish
로 바꿔 줄수 있다. 바꿔 주는게 좋을것 같다.
그리고 이렇게 에러가 뜨는건 Cannot GET /users/github/finish
Router
에 /users/github/finish
URL
이 없기 때문이다.
그리고 다시 새로고침을 해주고 Login
에서 Continue with Github
로 가면
그럼 이제 더 이상 물어보지 않고 바로 웹사이트로 돌려 보내주고 있다.
왜냐하면 Github
는 "yse"
라고 한걸 기억하고 있기 때문에 다시 또 물어볼 필요가 없다.
그러면 이제 /users/github/finish
를 만들어 본다.
userRouter.get("/github/start", startGithubLogin);
userRouter.get("/github/finish", finishGithubLogin);
export const finishGithubLogin = (req, res) => {};
export const edit = (req, res) => res.send("Edit User");
export const remove = (req, res) => res.send("Remove User");
export const logout = (req, res) => res.send("Log out");
export const see = (req, res) => res.send("See User");
이번 파트에서 user
를 Github
로 보내줬고 URL
을 좀 더 보기 좋게 만들어 줬다.
이제 user
를 code
와 함께 돌려 보내 줄거다.
다음 파트에서는 user
정보를 가져올수 있께 code
를 사용해 볼거다.