github λ‘κ·ΈμΈ κ΅¬ννκΈ°
scope / URLSearchParams / node-fetch / access token
Authorizing OAuth Apps μ°Έκ³
π‘ web application flow
π userκ° GitHub μ μμ μμ²νλλ‘ λ¦¬λλ μ λλ€.
π userκ° μ±μ μΉμΈνλ©΄ GitHubμ μν΄ λ€μ μΉ μ¬μ΄νΈλ‘ 리λλ μ λλ€.
π μ±μ΄ userμ access tokenμ μ΄μ©ν΄ APIμ μ‘μΈμ€νλ€.
λ¨Όμ , github - developer settings δΈ OAuth appsμμ WetubλΌλ μ΄λ¦μ OAuth μ±μ λ§λ€μ΄μΌ νλ€.
κ·Έ μ€μμ Authorization callback URLμ΄λ userκ° μ±μ μΉμΈνμ λ githubκ° userλ₯Ό redirect μν€λ urlμ λ§νλ€.
μ±μ λ§λ ν client IDλ₯Ό νμΈν μ μλ€.
GET https://github.com/login/oauth/authorize
Authorizing OAuth Appsμ μλ΄λ₯Ό μ°Έκ³ ν΄ login.pug νμΌμμ githubμμ μ£Όλ urlλ‘ μ°κ²°λλ λ§ν¬λ₯Ό λ§λ λ€.
κ·Έλ¬λ μλ λ§ν¬λ§μΌλ‘λ ν΄λ¦ μ 404 μλ¬κ° λ¬λ€.
//- login.pug
li
a(href="https://github.com/login/oauth/authorize") Continue with Github →
client_id (νμ)
url λμ Wetube μΉ μ¬μ΄νΈμ client_id νλΌλ―Έν°λ₯Ό μΆκ°ν ν λ€μ λ§ν¬λ₯Ό ν΄λ¦νλ©΄ (μ΄λ―Έ githubμ λ‘κ·ΈμΈ λμ΄ μλ userμ κ²½μ°) Wetube μΉ μ¬μ΄νΈλ₯Ό μΉμΈν κ²μΈμ§ 묻λ νμ΄μ§κ° λμ¨λ€.
μΉμΈνκΈ°λ₯Ό ν΄λ¦νλ©΄ Wetubeλ ν΄λΉ userμ μ μκ³Ό νλ‘ν λ±μ public λ°μ΄ν°λ₯Ό μ»μ μ μλ€.
li
a(href="https://github.com/login/oauth/authorize?client_id=79a813159e30dd338d2b") Continue with Github →
scope
Scopes for OAuth Apps - Available scopes μ°Έκ³
userμ private λ°μ΄ν°κΉμ§ μ»μ΄μ€κΈ° μν΄μλ scope νλΌλ―Έν°λ₯Ό μ΄μ©ν΄μΌ νλ€.
μ΄λ₯Ό μ΄μ©ν΄ userλ‘λΆν° μΌλ§λ λ§μ μ 보λ₯Ό κ°μ Έμ¬ κ²μΈμ§ μ€μ ν μ μλ€.
scope λͺ©λ‘μ 곡백μΌλ‘ ꡬλΆνμ¬ μ μ μ μλ€.
li
a(href="https://github.com/login/oauth/authorize?client_id=79a813159e30dd338d2b&scope=read:user user:email") Continue with Github →
allow_signup
κΈ°λ³Έκ°μ trueμ΄λ―λ‘ userκ° github κ³μ μ΄ μλ€λ©΄ gihub κ³μ μ μμ±ν μ μλ λ§ν¬κ° νμλλ€.
false κ°μ μ£Όλ©΄ νμλμ§ μλλ€.
li
a(href="https://github.com/login/oauth/authorize?client_id=79a813159e30dd338d2b&scope=read:user user:email&allow_signup=false") Continue with Github →
π‘ userμ github μ μ μμ²νκΈ°
urlμ κΈ°λ°νλ€
π μμ μ±μ μ£Όλ urlμ λ°μ λ§ν¬λ₯Ό λ§λ λ€
π κ·Έ urlμ μμ μ±μ΄ μ£Όλ idλ₯Ό μ λλ€ (νμ)
π κ·Έ μΈ scope(permission) λ±μ νλΌλ―Έν°λ₯Ό μΆκ°νλ€
κ·Έλ¬λ, μ΄λ κ² μμ±νλ©΄ ν΄λΉ λ§ν¬κ° νμν κ³³μ λ§€λ² κΈ΄ urlμ 볡λΆν΄μΌ νκ³ μΆν μμ λ μ΄λ ΅λ€.
μ½λλ₯Ό κ°μ νκΈ° μν΄ λ§ν¬μ urlμ λ°κΏμ€ ν ν΄λΉ routeμ startGithubLogin 컨νΈλ‘€λ¬λ₯Ό μΆκ°νλ€.
//- login.pug
a(href="/users/github/start") Continue with Github →
// userRouter.js
import { startGithubLogin } from "../controllers/userController";
userRouter.get("/github/start", startGithubLogin);
config objectμ νλΌλ§ν° μ΄λ¦κ³Ό κ°λ€μ λ΄λλ€.
new URLSearchParams(config).toString()
λ₯Ό μ΄μ©ν΄ config objectλ₯Ό νλΌλ―Έν° ννλ‘ λ°κΏ μ μλ€.
export const startGithubLogin = (req, res) => {
const baseUrl = "https://github.com/login/oauth/authorize";
const config = {
client_id: "79a813159e30dd338d2b",
allow_signup: false,
scope: "read:user user:email"
};
const params = new URLSearchParams(config).toString();
const finalUrl = `${baseUrl}?${params}`;
return res.redirect(finalUrl);
};
userκ° githubμ μ± μΉμΈ νμ΄μ§μμ μΉμΈνκΈ°λ₯Ό ν΄λ¦νλ©΄, githubλ Authorization callback URLλ‘ userλ₯Ό redirect μν¨λ€.
μ΄λ κ·Έ urlμ codeλ₯Ό ν¨κ» 보λ΄λλ° μ΄λ₯Ό access tokenμΌλ‘ λ°κΏ github APIμ μ κ·Όν¨μΌλ‘μ¨ userμ λν μ 보λ₯Ό κ°μ Έμ¬ μ μλ€.
λ¨Όμ githubκ° μ€ codeλ₯Ό access tokenμΌλ‘ λ°κΏμΌ νλ€.
μΌλ¨μ Authorization callback URLμ routeμ μΆκ°νκ³ finishGithubLogin 컨νΈλ‘€λ¬λ μΆκ°νλ€.
μμ§μ μ무 μλ΅λ ν΄μ£Όμ§ μμ μνμ΄λ€.
// userRouter.js
userRouter.get("/github/finish", finishGithubLogin);
// userController.js
export const finishGithubLogin = (req, res) => {};
μλμ urlλ‘ client_id
, client_secret
, code
μ ν¨κ» POST requestλ₯Ό 보λ΄μΌ νλ€.
POST https://github.com/login/oauth/access_token
client_idλ μ€λ³΅νμ¬ μ¬μ©λκΈ° λλ¬Έμ μ΄λμλ μΈ μ μλλ‘ .env νμΌμ μΆκ°ν΄ μ¬μ©νλλ‘ νλ€.
client_secretμ μ½λμ ν¬ν¨λμ΄μλ μλκΈ° λλ¬Έμ .env νμΌμ μΆκ°ν΄ μ¬μ©νλλ‘ νλ€.
codeλ githubκ° μ£Όλ codeλ‘μ urlμ νμλλ€. req.query.codeλ₯Ό ν΅ν΄ μ»μ μ μλ€.
GH_CLIENT=sf2ls315ddwg2gj38d2a
GH_SECRET=fdfd2f325dfdfdf1dfdfd
finishGithubLogin 컨νΈλ‘€λ¬λ₯Ό μμ
fetchλ₯Ό μ΄μ©νλ©΄ μ€μ λ‘ urlμ λ°©λ¬Ένμ§ μκ³ λ κ·Έλ‘λΆν° λ°μ΄ν°λ₯Ό κ°μ Έμ¬ μ μλ€. [TIL] 211108 μ°Έκ³
// userController.js
export const finishGithubLogin = async (req, res) => {
// finalUrlμ λ§λ¦ β
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 URLSearchParmas(config).toString();
const finalUrl = `${baseUrl}?${params}`;
// finalUrlλ‘λΆν° λ°μ΄ν°λ₯Ό κ°μ Έμ΄ β (finalUrlμ POST requestλ₯Ό 보λ)
const data = await fetch(finalUrl, {
method: "POST",
headers: { // githubλ‘λΆν° jsonμ return λ°κΈ° μν΄ μΆκ°ν΄μΌ ν¨
Accept: "application/json",
},
});
// κ·Έ λ°μ΄ν°λ₯Ό json νμμΌλ‘ λ°κΏ β
const json = await data.json();
return res.send(JSON.stringify(json)); // λΈλΌμ°μ μμ νμΈνκΈ° μν΄ μμλ‘ μμ±ν μ½λ
};
κ·Έλ°λ° fetchλ κΈ°λ³Έμ μΌλ‘ νλ‘ νΈμλμμ μ¬μ©μ΄ κ°λ₯ν ν¨μμ΄λ―λ‘ λ°±μλμμ fetchλ₯Ό μ¬μ©ν μ μλλ‘ μΆκ°λ‘ μ€μ μ ν΄μΌ νλ€.
node-fetch μ€μΉ
fetchλ₯Ό μ€μΉν ν userController.js νμΌμμ fetch λΌλ μ΄λ¦μΌλ‘ import νλ€.
μ΅μ λ²μ μΌλ‘ μ€μΉνλ©΄ μλ¬κ° λ¬λ€. 2.6.1 λ²μ μΌλ‘ μ€μΉν κ².
$ npm i node-fetch@2.6.1
// userController.js
import fetch from "node-fetch";
μ΄μ login νμ΄μ§μμ 'Continue with Github β'λ₯Ό ν΄λ¦νλ©΄, νλ©΄μ μλμ κ°μ΄ λ¬λ€.
github λ°±μλλ‘ POST μμ²μ λ³΄λ΄ access_tokenμ μ»κ² λ κ²μ΄λ€.
{
access_token: "sho_sdfwdBJhosd7kgIp5Usfl30swRUsHEwpJ9df",
token_type: "bearer",
scope: "read:user,user:email"
}
access tokenμΌλ‘ github APIλ₯Ό μ¬μ©ν΄ userμ λν μ 보λ₯Ό κ°μ Έμ¬ μ μλ€.
github API urlμ GET requestλ₯Ό 보λ΄λ©΄μ μΈμ¦μ μν΄ access_tokenμ 보λ΄λ©΄, urlλ‘λΆν° userμ λν μ 보λ₯Ό λ°μμ¬ μ μλ€.
Authorization: token OAUTH-TOKEN
GET https://api.github.com/user
λ¨Όμ , json objectμ 'access_token'μ΄ μμ λμ μμ κ²½μ°μ μμ κ²½μ°λ₯Ό λλμ΄ finishGithubLogin 컨νΈλ‘€λ¬λ₯Ό λ€μκ³Ό κ°μ΄ μμ νλ€.
+await μμ awaitμ λ£λ ννλ‘ μ½λλ₯Ό μμ νλ€.
// userController.js
export const finishGithubLogin = async (req, res) => {
// finalUrlμ λ§λ¦
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}`;
// finalUrlλ‘λΆν° λ°μ΄ν°λ₯Ό κ°μ Έμ΄ (finalUrlμ POST requestλ₯Ό 보λ)
// κ·Έ λ°μ΄ν°λ₯Ό json νμμΌλ‘ λ°κΏ
const tokenRequest = await (
await fetch(finalUrl, {
method: "POST",
headers: {
Accept: "application/json",
},
})
).json();
// access tokenμ μ΄μ©ν΄ github APIμ μ κ·Όν΄ userμ λν μ 보λ₯Ό κ°μ Έμ΄
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
const userData = await (
await fetch("https://api.github.com/user", {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData); // user μ 보 νμΈμ μν΄ μμλ‘ μμ±ν μ½λ
} else {
return res.redirect("/login");
}
};
μ΄μ login νμ΄μ§μμ 'Continue with Github β'λ₯Ό ν΄λ¦νλ©΄, μ½μ μ°½μ githubκ° κ°μ§κ³ μλ userμ λν μ 보(public λ°μ΄ν°)κ° λ¬λ€.
κ·Έλ°λ°, μ°λ¦¬κ° νμν emailμ κ°μ΄ nullμ΄λΌκ³ λ¬λ€.
μ΄λ₯Ό λλΉνμ¬ λ λ€λ₯Έ requestλ₯Ό λ§λ€μ΄μΌ νλ€.
π‘ githubμμ userμ email μ 보 κ°μ Έμ€κΈ°
github - reference - Users - Emails μ°Έκ³
userμ emailμ κ°μ Έμ€κΈ° μν΄ get requestλ₯Ό μ΄λ€ urlλ‘ λ³΄λ΄μΌ ν μ§λ μ μ¬μ΄νΈλ₯Ό μ°Έκ³ ν΄ μ μ μλ€.
λΉμ°ν λκ°μ access_tokenμ μ¬μ©νλ€.
github API urlμμ λ°λ³΅λλ λΆλΆμ λ³μλ‘ λ§λ€μ΄μ£Όμλ€.
// access tokenμ μ΄μ©ν΄ github APIμ μ κ·Όν΄ userμ λν μ 보λ₯Ό κ°μ Έμ΄
if ("access_token" in tokenRequest) {
const { access_token } = tokenRequest;
const apiUrl = "https://api.github.com";
// public λ°μ΄ν°
const userData = await (
await fetch(`${apiUrl}/user`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
// private λ°μ΄ν° (δΈ email)
const emailData = await (
await fetch(`${apiUrl}/user/emails`, {
headers: {
Authorization: `token ${access_token}`,
},
})
).json();
console.log(userData, emailData); // user μ 보 νμΈμ μν΄ μμλ‘ μμ±ν μ½λ
} else {
return res.redirect("/login");
}
μ΄μ login νμ΄μ§μμ 'Continue with Github β'λ₯Ό ν΄λ¦νλ©΄, μ½μ μ°½μ userμ λν public λ°μ΄ν°μ private email λ°μ΄ν°κ° λͺ¨λ λ¬λ€.
ννΈ, κ·Έ μ€μμ verifiedμ΄λ©΄μ primaryμΈ emailμ μ°ΎμμΌ νλ€.
μ΄λ₯Ό λͺ¨λ λ§μ‘±νλ emailμ΄ μλ€λ©΄ userλ₯Ό login νμ΄μ§λ‘ λλ €λ³΄λΈλ€.
const email = emailData.find(email => email.primary === true && email.verified === true);
if (!email) {
return res.redirect("/login");
}
μ΄μ emailμ κ°μ Έλ€ μΈ μ μλ€.
κ·Έλ¬λ, μμ§λ μκ°ν΄μ€μΌ ν κ²λ€μ΄ λ¨μ μλ€.
μΉ μ¬μ΄νΈμμ emailκ³Ό passwordλ₯Ό μ
λ ₯ν΄ μ΄λ―Έ κ³μ μ μμ±ν userκ° githubλ‘ λ‘κ·ΈμΈνλ €κ³ ν λ μ΄λ»κ² ν κ²μΈκ°
λ±μ λ¬Έμ λ₯Ό ν΄κ²°ν΄μΌ νλ€.
user authentication ννΈ λ§λ¬΄λ¦¬
user ννΈ μ²μλΆν° λκΉμ§ λ€μ ꡬνν΄λ³΄κΈ°