카테고리 없음

로그인 공부(oauth2 + jwt)

코딩연습중 2025. 3. 24. 11:00

전에 spring security + oauth2+ jwt 로 로그인을 구현하려 한 적이 있었는데 뭔가 공부도 덜 된 상태에서 사용 목적이나 사용방법을 제대로 익히지 않은 채로 막 사용한 것 같아 조금 더 공부해보려고 한다.

 우선 첫번째 헷갈렸던 부분은 oauth가 jwt와 완전히 분리된 것인가? 일단 oauth의 개념에 대해 알아볼 필요가 있었다.

 

OAuth(Open Authorization)란?

OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹 사이트 상의 자신들의 정보에 대해

웹 사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는

접근 위임을 위한 개방형 표준

 

쉽게 말하면, 어플리케이션을 이용할 때 사용자가 해당 어플리케이션에 ID, PW등의 정보를 제공하지 않고,

신뢰할 수 있는 외부 어플리케이션(Naver, Google, Kakao, Facebook 등)의 Open API에

ID, PW를 입력하여 해당 어플리케이션이 인증 과정을 처리해주는 방식입니다.

 

Oauth의 의미는 위와 같다. 

jwt나 session은 사용자 개인정보를 입력받아 인증을 처리해주기 위한 수단이다.

 

헷갈렸던 부분은 어차피 oauth나 jwt, session은 사용자 인증을 위한 것인데 그 중에 하나만 쓰면 되는 것이 아닌가? 싶었다. 근데 정리해보면 OAuth는 인증 과정인 것이고, JWT 및 Session은 사용자 인증을 위한 수단인 것이다.

 

그렇기 때문에 oauth와 jwt 또는 oauth와 session이 같이 쓰인다.

 

두번째로 헷갈렸던 부분은 OAuth를 구현할 때 구글에 찾아봤을 때는 다양한 방법이 있었는데,

1. 프론트에서 모든 걸 해결하는 방법...(이건 보안 상 아예 고려하지도 않음)

2. 프론트에서 구글 로그인으로 이동한 후, 받아온 인가코드를 백으로 넘겨서 토큰 발급 & 사용자 정보 접근 및 이후의 로그인 프로세스 처리.

3. 프론트에서 인가코드요청 보내고 리다이렉트 URI를 백엔드로 설정(또는 그냥 백에 login 요청 후 인가코드 요청도 백에서)해서 백에서 모든 걸 처리

대략 이 세가지의 방법이었던 것 같다. (물론 이 외에도 존재하겠지만..)

 

찾아봤을 때 소스가 가장 많았던 건 두번째 방법이었는데, 과연 프론트에서 구글 로그인으로 이동해서 인가 코드를 받아오는 프로세스를 구현하는 게 보안 상으로 괜찮은 방법이 맞는건가.. 싶었다. 보안에 대한 지식이 별로 없는 상태에서 봐도 세번째가 가장 안전하고 프로세스도 깔끔하다고 생각되어 세번째 방법으로 OAuth 로그인을 구현했었다.(근데... 결론적으로 내가 쓴 코드는 사용되지 못함.....이유는 뒤에 나옴)

지금 공부해보려고 찾아보니 내 생각이 맞았던 것 같다.

(참고: https://devtalk.kakao.com/t/oauth/136448/3)

 

OAuth까지 구현하고 마지막으로 JWT를 발급해서 프론트로 보내주는 과정에서 마지막 고민이 생겼다. 

원래 이제 OAuthSuccessHandler에서 jwt 토큰 발급 후, response header에 값을 담은 후, response를 프론트 도메인의 특정 페이지로 redirect 하려했는데 생각해보니까 http response는 stateless해서 redirect 할 때 초기화가 되어버리는 문제가 발생했다. 따라서 방법이 두가지가 있었다.

1. 백에 jwt 토큰을 요청하는 api를 추가적으로 요청

2. 쿠키에 jwt 담기.

(3. 쿼리스트링은 적고싶지 않음)

 

내가 봤을 때는 2번이 더 나은 선택지라고 생각해서 팀원분들께 의견을 물었었는데 해결해보겠다고 하셔서 넘겼는데 그 분은 1번 방법을 썼었다가 중간에 어떤 문제가 생겨서 프론트에서 인가코드 생성 후 백으로 넘기는 방향으로,,, OAuth 처리 프로세스가 완전히 바뀌었다.....

 

지금 공부를 조금 더 한 상태에서 보니 BFF(Backend For Frontend)를 두었다면 아마 이런 상황에서 어렵지 않게 문제를 해결했을 것 같다.(물론 해당 프로젝트는 해커톤이었기 때문에 BFF 쓰지 않음) 앞으로 OAuth는 계속 쓰게 될 것이고 규모가 꽤 되는 웹 프로젝트라면 BFF를 두는 게 책임 분리도 명확해지고 프론트에서 api 호출하는 인터페이스도 단순해지고 보안적으로도 좋고,,, 아무튼 이점이 많기 때문에 웬만해서는 BFF를 쓰는 게 좋을 것 같다

 

 

+ 오랜만에 글을 정리하려고 다시 봤는데 상황에 따라 다르게 구현하면 될 것 같다. 단지 소셜 로그인을 위해서 BFF를 두는 것은 바람직하지 않은 방법인 것 같다. 이 문제 때문에 고민을 하다가 직접 token.html이라는 중간 페이지를 만들어서 다음과 같은 흐름으로 처리했다.

  1. 백엔드 → token.html:
    로그인 인증이 끝나면, 서버가 발급한 JWT 토큰을 token.html?accessToken=... 형태의 쿼리스트링으로 전달한다.
  2. token.html → 부모 창:
    token.html은 스크립트에서 window.opener.postMessage()를 사용해,
    이 페이지를 연 부모 창(프론트엔드 애플리케이션) 으로 토큰을 전송한다.
  3. 정리 및 종료:
    보안상 쿼리스트링을 지우기 위해 history.replaceState()로 URL을 정리한 뒤,
    window.close()로 팝업 창을 자동으로 닫는다.

 

 

 

 

 

 

 

 

 

 

 

로그인 관련 추가 공부 자료

https://velog.io/@dev-joon/%EC%9D%B8%EC%A6%9DAuthentication%EA%B3%BC-%EC%9D%B8%EA%B0%80Authorization