인프런 커뮤니티 질문&답변

김현진님의 프로필 이미지
김현진

작성한 질문수

스프링 시큐리티 OAuth2

OAuth2.0 코드 부여 방식 인가서버를 REST API 서버 형태로 구현 질문

작성

·

811

·

수정됨

0

안녕하세요.. 선생님.. 드디어 완강했습니다.

시간 엄청 걸리고 좀 힘들었네요.

 

다름이 아니라.

모바일에서 요청하기 위한 자체 인가서버 OAuth2.0 authorization code 부여 방식 구현을 REST API 서버 형태로 구현할 수 있는지요?

사용자 인증 시 웹 페이지 형태의 응답이 아닌 REST API 형태로 요청하고 응답을 받을 수 있도록 구현하려고 합니다. 원래는 인가서버에 사용자 로그인을 하고 동의해야 code 발급받고 클라이언트로 redirect 하여 클라이언트 서버가 대신 인가서버로 access token을 발급 받게 되는데..

 

문제가 이 부분이네요.

Spring Security 수업에서 ajax 방식으로 API 서버에 요청하면 보통 redirect 없이 응답만 받는 구조이다

라고 선생님이 알려주셨는데요. 이와같이 redirect 없이 REST API로 구현할 수 있는 방법이 있는지요?

인가서버 로그인은 form login 방식을 사용하지 않고 html 응답 없이 front end 단에서 fetch나 ajax API를 이용해서 POST 전송, json 형태로 username과 password 를 넣어서 요청하고 응답 처리를 할 수 있도록 커스텀하게 구현은 할 수는 있을 것 같긴 해요.. 동의화면은 그냥 false로 해서 안 나오게 하거나 아니면 login 완료 후 json 응답으로 동의가 필요하다는 응답을 주고 다시 POST 방식으로 동의하면 될 것 같긴하네요.

클라이언트 서버는 그냥 리소스서버 처럼 사용해서, front end에서 클라이언트 서버로 access token을 전달하여 사용자 정보를 json 응답으로 가져오면 될 것 같아요.

 

하지만 문제는 redirect 처리를 어떻게 해야 하는지 잘 모르겠네요.

인가서버에 임시코드 요청 시 json 응답으로 code를 주고 redirect 없시 다시 인가서버로 access token을 요청하는 방식으로 구현해야 하는지요?

질문하면서도 authorization code 부여 방식에서 이렇게 구현을 하는게 맞는지? 의문이 드네요..

이런 REST API OAuth2 인가서버라는게 표준 spec에 맞는지도 의문이고..

모바일에서 REST API 방식으로 요청하고 인가서버에서 json 형태의 응답이 표준 spec 이랑 안 맞는 것 같기도 하고.. 무조건 html 웹 페이지 형태의 응답 기반으로 구현해야 하는지요?

 

여기에는 resource owner password 방식이 딱 맞는 것 같긴 한데 Deprecated 되어서 구현하면 안될 것 같고. 일반적으로 어떻게 구현하는지 알고 싶네요. 이런 케이스가 있는지요?

어떻게 하면 좋을지 선생님 의견을 듣고 싶네요..

감사합니다.

 

ps 질문2. 클라이언 서버, 리소스서버로 딱딱 나누지 않고, 클라이언트 서버를 리소스서버 형태로 구현해서.. 사용자 인증 방식을 form login이 아닌 JwtDecoder 방식으로 인증 처리하여 클라이언트 서버가 서비스 정보를 주는 형태로 구현해도 되는지요?

 

 

답변 1

1

정수원님의 프로필 이미지
정수원
지식공유자

음 일단 정리를 하자면 이렇습니다.

클라이언트가 인가서버에게 임시코드를 요청하는 역할은 스프링 시큐리티의 필터가 하게 되고 인가서버가 코드를 생성해서 다시 클라이언트에게 302 응답을 주게 되면 클라이언트가 리다이렉션 즉 새로운 요청을 하게 됩니다.

그리고 이 요청은 access token 을 받아오는 요청이고 이 요청 또한 스프링 시큐리티의 필터가 하게 됩니다.

여기서 Rest 로 하든 아니면 폼 전송으로 하던 그건 크게 중요한 요소가 아닙니다.

Rest 로 하게 되면 보통 자바스크립트 비동기로 한다고 했을 때 인가서버로부터 code 응답을 받는 지점도 자바스크립트의 응답 영역이 될 것입니다. 근데 자바 스크립트도 브라우저를 경유해서 전달 되기 때문에 결국 클라이언트의 에이전트는 브라우저가 되고 브라우저는 다시 클라이언트 서버로 리다이렉트 해서 access tokenk 을 요청하게 될 것입니다.

우리가 postman 으로 요청을 한다고 했을 때를 가정한다면 rest 와 별 다를 것이 없습니다.

물론 Rest 와 Form 전송은 요청과 응답 구조에 있어 다르긴 하지만 내부적으로 OAuth2 인가 과정은 별 다를 게 없습니다.

일단 현재 폼 전송으로 하던 방식을 Rest 방식으로 무조건 진행해 보십시오

그러면서 나타나는 여러가지 오류나 체크해야 할 사항들을 하나씩 점검해 나가시면 됩니다.

그리고 이건 시스템이나 환경에 따라 구현하는 방식들이 다르기 때문에 어떤 표준방식이 있는 것은 아닙니다.

진행하면서 이해가 안되는 부분들 중심으로 질문해 주시면 좀 더 정확한 답변이 될 것 같습니다.

김현진님의 프로필 이미지
김현진
질문자

감사합니다. 선생님..

 

제가 질문을 잘 못 드렸네요^^;; 이어서 구체적으로 다시 질문 드리면..

  • Spring Security 수업에서 제가 드렸던 질문

https://www.inflearn.com/questions/868561/%EC%84%B9%EC%85%98-4-ajax-%EB%82%B4%EC%9A%A9-%EC%A4%91-front-end-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8-%EC%A7%88%EB%AC%B8

"비동기 요청을 했다면 redirect 응답을 하기 보다는 200 응답을 받고 이후 적절한 처리를 하는 것이 어떨까 합니다" 이렇게 답변을 해주셨는데..

 

기존 form login 에서 수행하는 브라우저 역할을 클라이언트 에이전트라고 칭하여 말씀 드리겠습니다.

응답을 html 이 아닌 무조건 REST API 형태의 json 응답으로 하려고 합니다.

일반적인 access token 요청 전 까지 1단계 흐름을 말씀드리면..

1) 에이전트가 클라이언트 서버로 code 요청을 한다.

  • GET /oauth2/authorization/{registrationId}

2) 클라이언트 서버는 에이전트로 인가서버의 code 요청 URL을 만들어 클라이언트 에이전트에 302 redirect 응답한다. OAuth2AuthorizationRequestRedirectFilter

3) 에이전트는 인가서버로 리다이렉트 되어 code를 요청한다.

4) 인가서버는 에이전트에 사용자 /login html 페이지 응답 또는 /login 페이지 302 리다이렉트 응답한다. 그리고 로그인을 성공하면 사용자 동의화면 페이지를 응답한다. OAuth2AuthorizationEndpointFilter

→ ※ 이 부분부터 인가서버가 에이전트로 응답을 어떻게 해야 하는 것인지? 의문 입니다.

5) 로그인 요청 후 동의 요청

UsernamePasswordAuthenticationFilter, OAuth2AuthorizationEndpointFilter

 

일반적으로 에이전트가 인가서버로 코드 요청 시 사용자 인증이 안 된 경우 form login 인 경우 인가서버는 사용자 login HTML 페이지를 리다이렉트 하거나 페이지를 응답합니다.

에이전트는 json 형태의 데이터 {"username": "user", "password": "pass123"} POST 로 요청해야 로그인에 성공하게 되는데..

에이전트는 로그인을 해야하는 지? 사용자 동의를 해야하는 지? 알지 못합니다.

즉 에이전트는 json 응답만 받아야 하기 때문에 이슈가 생기네요.

궁금한 것은 로그인 페이지나 동의 화면을 위한 302 응답을 하느냐? 아니면 302 응답을 하지 않고 에이전트가 알아서 판단하여 다시 POST 로그인 처리 요청, POST 동의 요청을 하느냐 입니다. 인가서버가 302 를 응답하느냐 하지 않느냐에 따라서 구현이 달라지게 될 것 같아요.

 

인가서버 사용자 인증 완료 후 로그인 페이지가 아닌 코드를 redirect 받으면 이후 2단계 access token 요청으로.. 그냥 클라이언트 서버로 그대로 redirect 요청되어 알아서 처리되기 때문에 문제가 없을 것 같아요.

code를 리다이렉트 받으면 2단계 부터는 에이전트가 알아서 클라이언트 서버로 요청할 것이고 클라이언트 서버가 알아서 인가서버로 access token 을 요청하고, 발급 받고 사용자 인증처리하여... 클라이언트 서버가 에이전트로 json {"authentication": "OK"} 200 응답하면 될 것 같아요.

 

 

 

 

 

 

 

 

정수원님의 프로필 이미지
정수원
지식공유자

사실 클라이언트가 인가서버로부터 코드를 받아오기 전에 사용자의 승인을 위해 로그인 페이지를 보여주고 사용자가 로그인하게 되면 인가서버가 클라이언트로 코드와 함께 리다이렉트 응답을 하는 부분은 JSON 통신으로 가능한 부분은 아닙니다.

로그인 화면은 클라이언트와 상관없이 인가서버의 영역이기 때문에 클라이언트가 비동기든, Rest 든 어떤 형식으로 요청을 하더라도 거기에 맞는 로그인 화면으로 이어질 수 있도록 인가서버에서 구현해야 할 문제입니다.

문제는 인가서버에서 로그인 화면으로 이동할 수 있도록 응답했는데 그 응답을 클라이언트가 페이지가 아닌 Body 로 바로 받아서 발생하는 문제입니다.

만약 인가서버가 비동기 요청이 왔을 경우 클라이언트에게 바로 응답하는 것이 아닌 로그인 페이지로 다시 리다이렉트 해서 로그인 화면을 팝업이 되었던, 모달 창이 되었던 그런 식으로 처리한 다음 사용자의 승인이 통과된다면 그 다음 최종 응답을 클라이언트로 보내 주게 된다면 클라이언트는 code 값을 JSON 으로 받게 될 지도 모르겠습니다.

그래서 이 부분은 클라이언트의 역할이기 보다는 인가서버에서 어떻게 구현하느냐에 따라 동기식 처리 혹은 비동기식 처리로 나누어 질 수 있을 것 같습니다.

일단 스프링 시큐리티에서는 비동기적인 흐름이 아닌 동기적 흐름의 관점에서 필터가 동작하고 있습니다.

결론적으로는 코드를 받아오거나 토큰을 받아오는 부분은 얼마든지 비동기 요청으로 응답을 받고 처리하는 것이 가능하나 로그인 처럼 화면이 필요한 부분은 비동기적인 처리가 어렵고 이 부분은 클라이언트가 아닌 인가서버쪽에서 처리해야 할 부분입니다.
스프링 시큐리티를 사용한다면 이 부분에 대해서는 별도의 커스트마이징이 필요할 수 있을 것 같습니다.
아니면 처음부터 로그인 과정을 자동으로 거칠 수 있도록 password 방식을 써야 할 수 도 있습니다.
정답을 말씀 드리지는 못한 것 같지만 진행하시는데 참조하시길 바랍니다.

김현진님의 프로필 이미지
김현진
질문자

답변 감사드립니다. 선생님

잘 배우고 있습니다.

password 방식은 deprecated 되어서 구현하면 안 될 것 같고..

password 방식으로 구현하지 않으면 결론은 둘 중 하나인 것 같아요.

  1. json 응답을 커스텀하게 정해야 할 것 같네요. 만약 인가서버에서 로그인이 필요하다면 로그인이 필요하다고 json 응답을 하거나, 동의가 필요하다면 요청 scope 동의 항목과 함께 동의를 하라고 json 응답하여, 다시 에이전트가 그 응답을 판단하여 거기에 맞게 로그인 요청을 하거나 동의 요청을 비동기로 하면 될 것 같네요. 만약 인가서버에서 code가 발급 되면 인가서버에서 기존대로 302 redirect code를 응답하여 에이전트는 그냥 header의 302 location 그대로 클라이언트 서버로 요청하면 되겠네요.. 실무에서 이런 구현 케이스가 있는지?는 잘 모르겠지만..

2. 아니면 비동기 요청을 하지 않고 기존 form login 처럼 동기 방식으로 처리를 해야겠네요. 즉 인가서버는 Rest API 형태의 커스텀 구현이 힘들다고 결론 내야 할 것 같아요.

 

김현진님의 프로필 이미지
김현진

작성한 질문수

질문하기