
OAuth2 Provider란, 우리가 손쉽게 사용하는 카카오 로그인 하기 같은 기능을 제공하는 서버라고 생각하면 되겠다.
즉, 내가 자체적으로 개발한 서비스가 있고 회원이 존재할 때, 나만의 OAuth2 를 만들 수 있는 것이다.
스프링에서는 Spring Authorization Server (SAS) 라는 프레임워크를 사용하면, 손쉽게 OAuth2 및 OIDC 스펙을 구현할 수 있다.
오늘은 Spring Authorization Server에 대해서 알아보자.
- Spring에서 제공하는 OAuth2.1 과 OIDC1.0의 명세에 맞게 구현할 수 있도록 인증/인가 등의 기능을 제공하는 프레임워크.
- Spring Security를 기반으로 하여 시큐리티 필터체인에서 동작한다.
줄여서 SAS 라고 부른다.
이 프레임워크를 사용하기 위해서는 OAuth2.1 과 OIDC 와 같은 인증/인가 모델에 대해 먼저 알아야 한다.
OAuth2.1은 OAuth2.0 방식에서 약간의 보안을 더 개선한 버전이다.
OAuth2.0은 우리가 흔히 알고 있는 일반적인 '카카오 로그인' , '구글 로그인'이다.
이러한 동작의 흐름은 많아 알려져있다시피 아래와 같은 흐름으로 이루어진다.
~로 로그인 클릭authorization_code 와 state 값과 함께 클라이언트의 redirect_uri로 이동authorization_code를 통해 액세스 토큰 발급하지만 이 과정 중에서 authorization_code를 발급 받는 중에 가로채기 공격을 당할 수 있다.
이 문제를 해결하기 위해서 등장한 기법이 바로 PKCE (Proof Key Code Exchange)라는 기법이다.
여기서 추가로 두 가지 개념이 도입되는데 아래와 같다.
code_verifier : 애플리케이션(클라이언트)이 생성하는 일회용 비밀번호로, 클라이언트만 소유한다.code_challenge : 위 비밀번호를 해시 함수에 넣은 값authorization_code를 발급할 때 code_challenge를 OAuth 인증 서버에 같이 전달함으로써,
authorization_code 에 대한 자물쇠가 code_challenge임을 기록한다.
이후 클라이언트가 authorization_code 로 AccessToken을 발급받을 때,
자물쇠의 키인 code_verifier를 같이 보내야만, AccessToken을 발급할 수 있도록 한다.
즉, authorization_code 만 탈취해서는 이제 AccessToken을 발급받을 수 없다.
공격자는 code_verifier 까지 알아내야 한다.
OAuth2.1에 추가된 내용이 PKCE 뿐만은 아니다. OAuth2.0 방식의 문제점을 해결하기 위해서, 아직도 많이 수정되고 개선하고 있다고 한다.
OAuth2.1에 더 자세한 내용은 여기를 참고하면 되겠다.
OIDC는 OpenID Connect라는 뜻으로 OAuth2.0 위에 얇은 인증 기능을 추가한 것이다.
OAuth2.0 과정에서 AccessToken을 발급한 것은 애플리케이션에게 API를 호출하는 권한을 주는 것이다.
하지만 OIDC에서는 IDToken이라는 개념이 추가된다.
IDToken즉 IDToken이 등장함으로써 OIDC Client는 사용자의 신원 정보를 조회하는 API를 호출할 필요가 없고, IDToken에 담긴 사용자의 신원 정보를 바로 확인할 수 있는 것이다.
(네트워크 요청이 줄어들어 더 빨라지는 것이다.)
한글로 직역하면, 인증 서버와 자원 서버로 부를 수 있는데 역할은 아래와 같다.
Authorization Server
인증 서버는 말 그대로 인증을 수행하는 서버이다.
OAuth 클라이언트와 OIDC 클라이언트 모두 인증/인가를 담당한다.
authorization_code 및 AccessToken 발급Resource Server
리소스 서버는 토큰을 검증하고, 권한과 정책에 맞게 보호된 자원을 제공하는 역할을 한다.
요약하자면 우리가 authorization_code 와 액세스 토큰을 발급 받는 것은 Authorization Server 로부터 하는 것이고
카카오로부터 사용자의 정보를 조회하는 것은 Resource Server 로부터 하는 것이다.
OAuth 와 OIDC 의 개념을 어느정도 이해했다면, 이를 실제로 구현해볼 수 있다.
바로 Spring에서 제공하는 Authorization Server를 활용하면, OAuth2.1 및 OIDC Provider 를 구축할 수 있다.
SAS는 Spring Security 위에서 동작하기 때문에, Security가 수행하는 인증/인가의 처리 과정 등을 알아놓아야 한다.
SAS는 내부적으로 어떠한 과정을 거쳐서 OAuth2.1 및 OIDC 를 구현하는지 알아보자.
OAuth2.0 및 OAuth2.1 에서 authorization_code 를 발급 받고 AccessToken을 발급 받을 때 API를 호출하는 것처럼, SAS에서도 기본 엔드포인트를 제공한다.
물론 커스텀이 가능하다.
| 엔드포인트 | 용도 |
|---|---|
/oauth2/authorize | 클라이언트가 사용자의 동의를 얻기 위해서 접근하는 엔드포인트 |
/oauth2/token | 클라이언트가 authorization_code를 AccessToken으로 교환하는 엔드포인트 |
/oauth2/introspect | 토큰이 유효한지 검사하는 엔드포인트 (리소스 서버가 호출한다.) |
/oauth2/jwks | JWT 서명 검증에 필요한 공개키를 노출하는 엔드포인트 |
SAS에서는 내부적으로 모두 JWT를 사용한다.
이때 Resource Server 와 Authorization Server 가 분리되어 있을 경우,
Resource Server는 클라이언트로부터 받은 JWT를 해독하기 위해,
Authorization Server 와 통신하여 공개키 교환을 수행해야 한다.
이때 사용하는 키가 바로 JWK(JSON Web Key)이다.
(인터넷 상에서도 Key를 전달하기 위해 만들어진 데이터)
여기서 ResourceServer 와 AuthorizationServer 가 분리되어 있다보니,
AccessToken이 유효한지 확인하기 위해서 AuthorizationServer 키가 필요하다.
비대칭키 방식을 사용해서, AuthorizationServer의 비밀키로 JWT를 서명하는 경우에
AccessToken의 유효성을 검증할 때 AuthorizationServer의 공개키가 필요하다.
그래서 ResourceServer는 AuthorizationServer에게 /oauth2/jwks 엔드포인트로 요청을 보내서,
JWK 형태로 공개키를 받아온다.
자세한 동작 과정은 아래의 그림과 같다.

이제 SAS 내부에서 어떤 컴포넌트, 빈들이 OAuth2.0 / 2.1 , OIDC에 관여하는지 알아보자.
다 설명하면 너무 많으니까 핵심적인 부분만 알아보자.
RegisteredClientRepository
client_id , client_secret , redirect_uri 등을 저장한다.OAuth2AuthorizationService
AuthorizationEndPointFilter
/oauth2/authorize 요청을 처리한다.authorization_code를 발급한다.TokenEndpointFilter
/oauth2/token 요청을 처리한다.JwtEncoder / JwtDecoder
OAuth2TokenCustomizer
AuthetnicationManager , AuthenticationProvider , AuthenticationFilter 메커니즘을 똑같이 사용해서 OAuth2를 구현한다.