-
[OAuth 2.0] NodeJS로 SNS 로그인 하기웹프로그래밍/Back-end 2020. 12. 15. 19:09
벌써 취준을 시작한지도 언 4개월 정도가 되었다. 중간에 개인적으로 남들이랑 하는 프로젝트를 하고,
또 개인 프로젝트로 이 전에 학교에서 진행했던 프로젝트를 진행했더니 시간이 벌써 이렇게....!
취업을 해야 실력이 더 오를텐데 얼른 포트폴리오를 완성하고 취업해야지.
현재 만드는 프로젝트는 NEXT.js + REACT.js 를 이용한 프론트엔드와
Node.js + Express.js(Sequelize ORM) + MySQL+ Redis를 이용한 백엔드 시스템으로 구축되어 있다.
시스템 구조
1 USER
디바이스를 통하여 웹브라우저로 접속을 한다.
2 FRONT END
프론트 서버에서 HTML+CSS 등을 React.js 컴포넌트로 만든다. 그리고 이 화면을 사용자에게 전송한다.
이때, 사용자 요청이나 Page 접근 시, 사용자가 원하는 정보를 axios를 통해 서버 요청을 한다.
3 BACK END
쿼리를 이용하여 데이터베이스에 데이터를 요청한다.
OAuth 란 무엇인가
위키 백과 : OAuth [https://ko.wikipedia.org/wiki/OAuth]
"인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는 접근 위임을 위한 개방형 표준이다"
한 마디로 신뢰성 있는 사이트를 통하여 사용자 정보를 받아, 사용자 정보에 필요한 일련의 행위들을 애플리케이션 상에서 처리할 수 있도록 도와주는 아주 편리한 표준이다.
비교적 우리에게 익숙한 개념은 아이디를 입력하고 비밀번호를 입력하는 일이다. 하지만 많은 사이트에서 이러한 정보들이 탈취되고 있다. 또한, 사용자 입장에서는 가입하는 사이트가 정확하게 신뢰성 있는 사이트인지 역시나 알기 어렵다. 그럴 때, 사용자로부터 좀 더 신뢰성을 얻을 수 있는 대안이 OAuth를 통한 회원가입 및 로그인이 될 수 있다. (그리고 일단 편하다는게 중요😉)
OAuth 2.0 이란 무엇인가
"제 3의 앱이 자원의 소유자인 서비스 이용자를 대신하여 서비스를 요청할 수 있도록 자원 접근 권한을 위임하는 방법"
뒤에 붙은 숫자를 본다면 알겠지만 OAuth 1.0 을 이용하여 좀 더 사용성과 보안성, 그리고 다양한 디바이스에서 활용할 수 있도록 고안된 방법이다. 인터넷으로 검색을 해 보면 알겠지만 가장 대표적인 차이는 액세스토큰의 사용인데, 액세스 토큰에 유효기간을 지정하여 사용할 수 있도록 하는 것이다.
OAtuh 2.0 사용 시의 보안
결국 개념적으로 구축해야할 것은 바로 액세스토큰과 이를 관리할 서버이다. 하지만 최근에는 액세스 토큰의 탈취 위험으로 인하여 리프레쉬 토큰을 함께 사용한다. 이때, 리프레쉬 토큰은 액세스 토큰의 유효기간을 짧게함으로써 탈취당했을 때의 위험을 낮춘다. 하지만 의문이 들 것이다. 탈취당하면 그만인데 뭐가 위험을 낮춰? 라고. 나도 그런 의문이 제일 많이 들었다. 나도 사용할 때 편해보여서 도입 해 보고 싶긴한데 이게 뭐가 안전한데? 라는 생각이 들었다.
여기저기서 검색을 결과 해 본 결과 어떤 점이 안전한지는 아래와 같다.
(더욱 자세한 내용이 궁금하다면 OAuth 2.0과 관련된 Document를 찾아보는 것도 좋다. 내가 봤을 때는 약간 전공 공부 시간에 교수님이 만든 PPT를 보는 듯한 간결한 설명만 있었다ㅠㅠ.. 그래서 블로거들마다 사용법도 다양하다. 애초에 프로토콜이기 때문에 정해진 것은 없고, 어떻게 사용하는지에 대한 방법론? 규칙만 작성되어 있다. 프로토콜을 지켜 작성하되, 결국 구축의 문제는 개발자의 몫인 것이다.)
AcessToken + RefrashToken이 어떤 점에서 비교적 안전할까
1. 액세스 토큰의 유효기간을 짧게 설정하여 탈취하더라도 만료된 토큰은 사용할 수 없다.
2. 액세스 토큰을 재발급 받기 위해서는 액세스토큰으로 승인 된 사용자 임을 확인하고, 리프레쉬 토큰을 이용한 재발급만 받을 수 있도록 할 수 있다.
3. 액세스 토큰의 유효기간은 일반적으로 10분, 리프레쉬 토큰은 14~30일 사이의 유효기간을 두어 오랜 기간 사용하지 않았던 사용자의 토큰 탈취 위험을 막는다.하지만 의문이 들 수 있다. 그럼 오랜 기간 접속했던 사용자는 어떻게 해? 리프레쉬 토큰도 액세스 토큰도 다 만료되어버리면 영원히 접속 못하는 건가? 라고. 하지만 이는 다시 재접속을 하면 재발급받을 수 있다.
구글에 아이디나 비밀번호 저장 해 놓고, 날라가면 재접속하여 다시 아이디나 비밀번호 저장하는거랑 같다.
물론 100% 완벽한 보안은 없다. 둘 다 탈취의 위험성은 늘 있다. 그렇기 때문에 토큰 자체에 유저에게 위협이 될 만한 정보는 담지 않는 것이 원칙이고, .env 같은 보안과 관련된 정보 및 시크릿 키가 노출되지 않는 것이 최우선이다.
OAuth 2.0 구축하기
구축에 필요한 알고리즘은 액세스토큰 및 리프레쉬 토큰의 인증 및 발급이다. 또한 인증할 때, 관련알고리즘은 미들웨어로 사용하여 API 요청이 올 때, 인가 된 사용자에게만 정보를 전달할 수 있도록 해야 한다.
구축에 필요한 알고리즘 2가지
[1] VerifyToken
[2] SignTokenSNS 서비스를 이용한 OAuth 2.0 인증을 하고 싶다면 여기에 추가적인 요소가 더 필요하다.
추가로 해야할 일
1. 본인이 사용할 SNS 서비스에서 클라이언트 ID 및 클라이언트 시크릿 키를 발급받는다.
2. 정보를 .env로 저장한다.
3. passport와 OOOStrategy 를 이용하여 로그인을 처리한다. (이는 좀 더 쉽게 로그인을 처리하도록 돕는다)예시) 구글 로그인 전략
비교적 알고리즘은 간단하다. 일단 나의 경우엔 구글에서 전달 해 준 유저 아이디를 우리의 서비스에 이미 가입된 사용자인지 확인하고, 가입 된 경우, 이미 유저임을 알리는 메시지를 전달하여 처리하고, 유저가 아닌 경우, 가입을 하고 유저 정보를 전달하는 방식으로 작성하였다.
그리고 이를 이용하여 본인이 원하는 처리 방식으로 처리할 수 있다. 나는 정보를 이용하여 우리 사이트에 가입을 시키는 방법이지만, 직접 SNS 서비스에서 액세스토큰만 받아서 사용하는 방법도 있다.
(물론 그 사용법은 내가 사용하지 않은 방법이라 모른다)
const passport = require("passport"); const { verifyUser, insertUser } = require("../controllers/user.controller"); const GoogleStrategy = require("passport-google-oauth20").Strategy; module.exports = () => { passport.use( new GoogleStrategy( { clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: "/auth/google/callback", proxy: true, }, async (accessToken, refreshToken, profile, done) => { try { const exUser = await verifyUser(profile.id); if (exUser) { return done(null, exUser, { message: "User" }); } else { const newUser = await insertUser(profile, profile.provider); return done(null, newUser); } } catch (err) { return done(err, false, { message: err }); } } ) ); };
개인적으로 단순히 SNS 로그인을 구축하고 싶은 것이라면 파이어베이스를 이용한 방법이 가장 간단하고 쉽다.
한번 따라해보니 굉장히 어썸한 방법이었다. 🥳🥳
지금 생각해보니 파이어베이스로 DB를 쓸 걸 그랬나라는 생각도 든다.
오늘은 OAuth 2.0에 대해서 공부해보았다. 역시 알면알수록 정답이 없으니 어려운 느낌이 든다.
그래도 구축하고 나니 은근 뿌듯. 현업에서는 어떻게 쓰는지 궁금하다.
이 다음엔 서버에서 구축하는 방법을 알아보았으니, 프론트엔드에서는 어떻게 저장하고 사용할지를 알아봐야겠다.