OAuth 에서 총 4가지 요소가 상호작용하며 작동한다.
이제 위와 같은 4가지 구성 요소들이 어떻게 OAuth 를 처리하는지 알아보자.
세부적인 작동 과정 이전에 간략하게 과정을 요약하면 다음과 같다.
이제 이러한 과정을 사용자가 Velog 서비스를 Github 소셜 로그인을 하는 시나리오로 알아보자. (내부적으로는 100% 동일하지 않을 수 있지만, 전체적인 과정을 이해하는데 예시를 든 것)
Client ID
와 Client Secret
, Callback URL
등을 설정하여 유효한 Github OAuth 로 접근했는지 검증한다.client_id
, redirect_uri
, login
, scope
, state
, allow_signup
Callback URL
(redirect_uri
) 의 code 쿼리스트링에 인가 코드를 포함하여 리다이렉트된다.Client ID
, Client Secret
, Callback URL
등과 함께 인가 코드를 인가 서버에 POST 메소드로 요청한다.Settings
- Developer settings
- OAuth Apps
를 순서대로 클릭Register a new application
버튼을 클릭Application name
칸에는 어플리케이션 이름을 작성한다. 이름에는 민감 정보를 제외하고 공개해도 되는 이름으로 작성한다.Homepage URL
칸에는 해당 어플리케이션의 전체 URL을 작성한다.Application description
칸에는 설명을 적고 싶다면 작성한다.Authorization callback URL
칸에는 인가 과정을 거친 후 인가 코드를 포함하여 리다이렉트 될 URL을 작성한다.마지막 Enable Device Flow
는 말 그대로 Device Flow를 사용할 지 체크하는 부분이다. Device Flow는 약간 작동 방식이 다른 것으로 보이며 자세한 내용은 Github OAuth 래퍼런스에 적혀있다. 이번 예제에서는 체크하지 않고 진행하였다.
Generate a new client secret
을 클릭하여 Client Secret 정보를 생성한다.Client ID
, Client Secret
, Authorization callback URL
을 기억하자.npx create-react-app my-oauth-app
cd my-oauth-app
npm install --save http-proxy-middleware
// src/setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/login", {
target: "https://github.com",
changeOrigin: true,
})
);
app.use(
createProxyMiddleware("/user", {
target: "https://api.github.com",
changeOrigin: true,
})
);
};
npm install --save styled-components
npm install react-router-dom
우리가 구현해야 할 페이지는 다음과 같다.
이를 위해서 간단히 로직을 설계하면
components/Login.js
위치에 아래와 같은 코드를 작성하였다.// components/Login.js
import React from "react";
import styled from "styled-components";
const Login = () => {
return (
<LoginPage>
<Title>로그인이 필요합니다.</Title>
<GithubLoginBtn>Login with GitHub OAuth</GithubLoginBtn>
</LoginPage>
);
};
const LoginPage = styled.div`
width: 300px;
height: 500px;
`;
const Title = styled.div`
width: 100%;
text-align: center;
font-size: 24px;
`;
const GithubLoginBtn = styled.button`
width: 100%;
height: 50px;
border-radius: 10px;
color: white;
background-color: hsl(210, 8%, 20%);
&:hover {
cursor: pointer;
}
`;
export default Login;
App.js
파일도 수정해주자.// src/App.js
import Login from "./components/Login";
function App() {
return <Login />;
}
export default App;
이제 구현해야 하는 부분을 살펴보자.
https://github.com/login/oauth/authorize
주소로 GET 요청export const CLIENT_ID = "8401b2ab1be3f5f33623";
export const CLIENT_SECRETS = "1eec25b6176d61a43bbb083b3e6770a45cfb3d39";
export const CALLBACK_URL = "http://localhost:3000/oauth/callback";
export const GITHUB_AUTH_CODE_SERVER = "/login/oauth/authorize";
export const GITHUB_AUTH_TOKEN_SERVER = "/login/oauth/access_token";
export const GITHUB_API_SERVER = "/user";
먼저 로그인 버튼을 클릭하면 GITHUB_AUTH_SERVER에 GET 요청을 전달하는 기능을 구현해보자.
// components/Login.js
// 생략
const Login = () => {
const AUTHORIZATION_CODE_URL = `${GITHUB_AUTH_CODE_SERVER}?client_id=${CLIENT_ID}&redirect_url=${CALLBACK_URL}`;
// 인가 서버에 GET 요청을 전송한다.
const fetchAuthCode = () => {
window.location.assign(AUTHORIZATION_CODE_URL);
};
return (
<LoginPage>
<Title>로그인이 필요합니다.</Title>
<GithubLoginBtn onClick={fetchAuthCode}>Login with GitHub OAuth</GithubLoginBtn>
</LoginPage>
);
};
// 생략
Authorize [닉네임]
을 클릭하게 되면 http://localhost:3000/oauth/callback?code=24fafb16019382cba7e4
와 같이 사전에 설정한 CALLBACK_URL
에 쿼리스트링으로 인가 코드가 추가되어 리다이렉트된다.CALLBACK_URL?code=인가코드
와 같은 주소로 리다이렉트 되어 있을 것이다.import { BrowserRouter, Route, Routes } from "react-router-dom";
import Login from "./components/Login";
import Callback from "./components/Callback";
function App() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/oauth/callback" element={<Callback />} />
</Routes>
</BrowserRouter>
);
}
export default App;
// components/Callback.js
import React, { useEffect } from "react";
import { CLIENT_ID, CLIENT_SECRETS, GITHUB_AUTH_TOKEN_SERVER } from "../constants/oauth";
const Callback = () => {
useEffect(() => {
const fetchAccessToken = async () => {
// 쿼리스트링에서 Authorization Code를 가져옵니다.
const location = new URL(window.location.href);
const code = location.searchParams.get("code");
const ACCESS_TOKEN_URL = `${GITHUB_AUTH_TOKEN_SERVER}?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRETS}&code=${code};
return fetch(ACCESS_TOKEN_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
};
fetchAccessToken()
.then((response) => response.json())
.then((data) => console.log(data))
.catch((err) => console.log(err));
});
return <div>로딩중 ...</div>;
};
export default Callback;
// components/Callback.js
// ...생략
fetchAccessToken()
.then((response) => response.json())
.then((data) => {
navigate("/profile", { state: data.access_token });
})
.catch((err) => console.log(err));
// src/App.js
// ...생략
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/profile" element={<Profile />} />
<Route exact path="/oauth/callback" element={<Callback />} />
</Routes>
</BrowserRouter>
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { GITHUB_API_SERVER } from "../constants/oauth";
const Profile = () => {
const location = useLocation();
const [userName, setUserName] = useState();
useEffect(() => {
const fetchGithubUser = () => {
const accessToken = location.state;
return fetch(GITHUB_API_SERVER, {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
},
});
};
fetchGithubUser()
.then((response) => response.json())
.then(({ login }) => setUserName(login))
.catch((err) => console.log(err));
});
return <div>로그인 된 사용자 : {userName ?? "로딩중.."}</div>;
};
export default Profile;
login
속성을 userName 상태로 보여주는 컴포넌트이다.가능하면 무작정 따라해볼 수 있는 좋은 글을 써보자!
라는 생각으로 시작했는데, 확실히 되게 어려운 것 같았다.. 구글에 많은 좋은 아티클들 정말 감사합니다..https://www.okta.com/kr/identity-101/whats-the-difference-between-oauth-openid-connect-and-saml/
https://developers.payco.com/guide
https://devkkiri.com/post/542d8419-6fb1-4e28-bbbe-d68f832fd78a
https://blog.naver.com/mds_datasecurity/222182943542
https://gist.github.com/ninanung/2ad24c760e81401ed65f13f634a25e73
LGTM
다음부터 승현님꺼 보면서 깃헙 OAuth 적용할래요