OAuth
- 웹이나 앱에서 흔히 찾아볼 수 있는 소셜 로그인 인증 방식은 OAuth 2.0라는 기술을 바탕으로 구현
- 전통적으로 직접 작성한 서버에서 인증을 처리해주는 것과는 달리, OAuth는 인증을 중개해주는 메커니즘
- 보안된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜
- 즉, 이미 사용자 정보를 가지고 있는 웹 서비스에서 사용자의 인증을 대신해주고, 접근 권한에 대한 토큰을 발급한 후, 이를 이용해 내 서버에서 인증이 가능해짐
- 인증을 다른 서비스에 맡길 뿐, 접근 권한 관리(Authorization)는 순전히 내 서버의 몫
소셜 로그인 로직
REACT_APP_CLIENT_ID=5d885f7ca3233abcf359
import React from "react";
import githubLogo from "./../images/github.png";
export default function Login() {
const CLIENT_ID = process.env.REACT_APP_CLIENT_ID;
const loginRequestHandler = () => {
return window.location.assign(`https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`);
};
return (
<>
<div className="left-box">
<span>
Education
<p>for the</p>
Real World
</span>
</div>
<div className="right-box">
<div className="right-box">
<h1>AUTH STATES</h1>
<h3>OAuth 2.0 소셜 로그인</h3>
<form onSubmit={(e) => e.preventDefault()}>
<div className="input-field">
<button type="submit" onClick={loginRequestHandler}>
<img id="logo" alt="logo" src={githubLogo} />
<span> LOGIN WITH GITHUB</span>
</button>
</div>
</form>
</div>
</div>
</>
);
}
import "./App.css";
import {BrowserRouter, Routes, Route} from "react-router-dom";
import Login from "./pages/Login";
import Mypage from "./pages/Mypage";
import {useEffect, useState} from "react";
import axios from "axios";
function App() {
const [isLogin, setIsLogin] = useState(false);
const [accessToken, setAccessToken] = useState("");
const getAccessToken = async (authorizationCode) => {
axios
.post("https://localhost:4000/callback", {authorizationCode})
.then((res) => {
setAccessToken(res.data.accessToken);
setIsLogin(true);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
const url = new URL(window.location.href);
const authorizationCode = url.searchParams.get("code");
if (authorizationCode) {
getAccessToken(authorizationCode);
}
}, []);
return (
<BrowserRouter>
<div className="main">
<div className="container">
<Routes>
<Route
path="/"
element={isLogin ? <Mypage accessToken={accessToken} setIsLogin={setIsLogin} setAccessToken={setAccessToken} /> : <Login />}
/>
</Routes>
</div>
</div>
</BrowserRouter>
);
}
export default App;
import axios from "axios";
import React, {useEffect, useState} from "react";
import Loading from "./components/Loading";
import User from "./components/UserInfo";
export default function Mypage({accessToken, setIsLogin, setAccessToken}) {
const [githubUser, setGithubUser] = useState(null);
const [serverResource, setServerResource] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const logoutHandler = () => {
axios
.delete("https://localhost:4000/logout", {data: {accessToken}})
.then((res) => {
setGithubUser(null);
setServerResource(null);
setIsLogin(false);
setAccessToken("");
})
.catch((err) => console.log(err));
};
useEffect(() => {
axios
.post("https://localhost:4000/userinfo", {accessToken})
.then((res) => {
const {githubUserData, serverResource} = res.data;
setGithubUser(githubUserData);
setServerResource(serverResource);
setIsLoading(false);
})
.catch((err) => console.log(err));
}, []);
return (
<>
<div className="left-box">
{!isLoading && (
<span>
{`${githubUser.login}`}님,
<p>반갑습니다!</p>
</span>
)}
</div>
<div className="right-box">
<div className="input-field">
{isLoading ? <Loading /> : <User githubUser={githubUser} serverResource={serverResource} logoutHandler={logoutHandler} />}
</div>
</div>
</>
);
}
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const fs = require('fs');
const https = require('https');
const controllers = require('./controllers');
const app = express();
const HTTPS_PORT = process.env.HTTPS_PORT || 4000;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
const corsOptions = {
origin: true,
methods: ['GET', 'POST', 'OPTIONS', 'DELETE'],
credentials: true,
};
app.use(cors(corsOptions));
app.post('/callback', controllers.callback);
app.post('/userinfo', controllers.userInfo);
app.delete('/logout', controllers.logout);
let server;
if (fs.existsSync('./key.pem') && fs.existsSync('./cert.pem')) {
const privateKey = fs.readFileSync(__dirname + '/key.pem', 'utf8');
const certificate = fs.readFileSync(__dirname + '/cert.pem', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
};
server = https.createServer(credentials, app);
server.listen(HTTPS_PORT, () => console.log(`🚀 HTTPS Server is starting on ${HTTPS_PORT}`));
} else {
server = app.listen(HTTPS_PORT, () => console.log(`🚀 HTTP Server is starting on ${HTTPS_PORT}`));
}
module.exports = server;
require("dotenv").config();
const axios = require("axios");
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
module.exports = async (req, res) => {
try {
const result = await axios({
method: "post",
url: `https://github.com/login/oauth/access_token`,
headers: {
accept: "application/json",
},
data: {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code: req.body.authorizationCode,
},
});
const accessToken = result.data.access_token;
return res.status(200).send({accessToken});
} catch (err) {
return res.status(401).send({message: "error"});
}
};
const axios = require("axios");
const serverResource = require("../../data/data.js");
module.exports = async (req, res) => {
const {accessToken} = req.body;
return axios
.get("https://api.github.com/user", {
headers: {
Authorization: `token ${accessToken}`,
},
})
.then((res) => res.data)
.then((githubUserData) => {
res.send({githubUserData, serverResource});
})
.catch((e) => {
res.sendStatus(403);
});
};
require("dotenv").config();
const axios = require("axios");
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
module.exports = (req, res) => {
const {accessToken} = req.body;
axios
.delete(`https://api.github.com/applications/${CLIENT_ID}/token`, {
data: {
access_token: accessToken,
},
auth: {
username: CLIENT_ID,
password: CLIENT_SECRET,
},
})
.then(() => {
res.status(205).send("Successfuly Logged Out");
})
.catch((e) => {
console.log(e.response);
});
};