[1. OAuth란]
OAuth("Open Authorization")는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다.
-위키백과
OAuth는 각 회사들의 파편화된 인증 방식(구글의 AuthSub, AOL의 OpenAuth, 야후의 BBAuth, 아마존의 웹서비스 API 등)을 한데 모아 표준화한 것으로, 이를 사용하면 이 인증을 공유하는 애플리케이션끼리는 별도의 추가 인증 없이 인증 내용을 사용할 수 있다.
따라서 우리는 OAuth를 따르는 애플리케이션을 개발해 자체적인 로그인 및 사용자 관리 기능을 구현할 필요 없이 다른 회사의 로그인 인증 정보만으로도 유저를 식별하고 관리할 수 있다.
[2. OAuth의 주체]
- Resource Owner - 리소스 소유자(사용자)
- Authorization Server - 인증 서버
- Resource Server - 리소스 서버(구글, 페이스북, 카카오 등)
- Client - 사용자를 대신해 인증 서버, 리소스 서버에 접근하는 애플리케이션(우리가 개발하려는 것)
[3. 동작 메커니즘]
- 사용자가 애플리케이션에 로그인 요청
- 애플리케이션은 사용자 요청을 받아 Client ID, Redirect URI, Response Type, Scope등의 매개변수를 쿼리스크링에 포함하여 인증 서버로 전달
- response type: 반환 타입을 지정. 반드시 code로 지정해서 요청해야 함(인증 성공 시 Authorization Code를 받기 때문).
- client id: 웹 서비스를 인증 서버에 등록했을 때 발급받은 id값
- redirect uri: 웹 서비스를 인증 서버에 등록했을 때 등록한 uri
- scope: Client가 부여받은 리소스 접근 권한(예: 구글의 주소록, 카카오의 생년월일, 프로필 사진 등
- 인증 서버에서 각 회사의 로그인 페이지 제공
- 사용자가 로그인 페이지에 ID와 비밀번호를 제출해 인증 서버에 직접 요청 전송
- 인증 성공 시 인증 서버는 기존에 Client에게서 받은 Redirect URI에 인증 코드를 포함시켜 사용자를 리디렉션
- Client는 다시 인증 서버에 인증 코드를 전달하고 Access Token을 발급받아 데이터베이스에 저장
- 이후 리소스 서버에서 값을 가져올 때마다 Access Token을 사용해서 접근
- 위 과정이 모두 끝나면 로그인 성공. scope 내에서 다양한 리소스 이용 가능
[4. 프론트엔드의 역할]
- github OAuth 서버로 github 로그인 요청 후, Authorization code 발급 받아 백엔드에 전달
- 백엔드에서 응답 받은 access token, refresh token 저장
- 권한이 필요한 요청마다 Authorization 헤더에 access token을 담아 전달
- access token이 만료되었다면, refresh token 보내서 갱신하기(프론트에서 요청 날릴 때 access token이 만료됨을 미리 판별하여 갱신 요청을 보낼 수 있음)
- refresh token 만료 기간이 7일 이내면, refresh token 재발급 요청
[5. React 구현 예시]
GoogleLoginPage.ts
const GoogleLogin = () => {
const navigate = useNavigate();
const handleNavigateBack = () => {
navigate(-1);
};
const handleLogin = () => {
// 구글 로그인 화면으로 이동시키기
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?
client_id=${process.env.REACT_APP_GOOGLE_AUTH_CLIENT_ID}
&redirect_uri=${process.env.REACT_APP_GOOGLE_AUTH_REDIRECT_URI}
&response_type=code
&scope=email profile`;
};
return (
<SignUpContainer>
<ArrowIcon src={Arrow} onClick={handleNavigateBack} />
<Copy>안녕하세요</Copy>
<Copy>구글 계정이 있나요?</Copy>
<Character src={Duck} />
<GBtn src={GoogleBtn} onClick={handleLogin} />
</SignUpContainer>
);
};
LoadingPage.ts
const Loading = () => {
const navigate = useNavigate();
// 이미 가입한 유저일 시 : 메인 페이지로 이동
const handleHome = () => {
navigate("/home");
window.location.reload();
};
// 처음 가입한 유저일 시 : 닉네임 설정 페이지로 이동
const handleNickName = () => {
navigate("/nickname");
window.location.reload();
};
// 현재 url에서 code 부분 추출
const params = new URLSearchParams(window.location.search);
const code = params.get("code");
const handleLoginPost = async code => {
const data = {
code: code,
};
try {
const res = await axios.post(
"https://server.bageasy.net/auth/login",
data,
);
// 토큰 localstorage에 저장
const accessToken = res.data.accessToken;
localStorage.setItem("bagtoken", accessToken);
// 신규/기존 회원 여부에 따라 페이지 이동
res.data.isExistingMember ? handleHome() : handleNickName();
} catch (error) {
console.log(error);
}
};
useEffect(() => {
if (code) {
handleLoginPost(code);
} else {
console.log("로그인 재시도하세요.");
}
}, [code, navigate]);
return (
<LoadingConatiner>
<LoadingIcon src={loading} />
<H2>로그인중입니다...</H2>
</LoadingConatiner>
);
};
+) react-oauth/google 사용
import { GoogleLogin } from "@react-oauth/google";
import { GoogleOAuthProvider } from "@react-oauth/google";
import jwt_decode from "jwt-decode";
const GoogleLoginButton = () => {
const clientId ="";
return (
<>
<GoogleOAuthProvider clientId={clientId}>
<GoogleLogin
onSuccess={credentialResponse => {
console.log(jwt_decode(credentialResponse.credential));
}}
onError={() => {
console.log("Login Failed");
}}
/>
</GoogleOAuthProvider>
</>
);
};
export default GoogleLoginButton;
구글 클라우드 플랫폼에서 클라이언트 ID를 발급받는 방법은 여기