OAuth 2가 동작하는 방법

12. OAuth 2가 동작하는 방법

12.1. OAuth 2 프레임워크

  • 타사 웹사이트나 웹이 리소스에 접근할 수 있게 허용하는 것이 주 목적인 프레임워크

HTTP Basic은 다음의 이유로 적합하지 않다.

  • 네트워크를 통해 자격 증명이 자주 공유된다.

  • 클라이언트가 자격 증명을 저장해 인증 및 권한 부여 요청과 함께 자격 증명을 서버에 보낼 수 있게 한다.

자격 증명을 여러 곳에서 새로 만드는 것보다 한 곳에서 관리하는 것이 편리하다. 이 역할을 하는 서버를 권한 부여 서버라고 부른다.

12.2. OAuth 2 인증 아키텍처의 구성 요소

  • 리소스 서버

    • 사용자가 소유한 리소스를 호스팅하는 서버

    • 리소스는 사용자의 데이터이거나 사용자가 수행할 수 있는 작업을 의미한다.

  • 사용자

    • 리소스 서버가 노출하는 리소스를 소유하는 개인

    • 대개 이름과 암호로 신원을 증명한다.

  • 클라이언트

    • 사용자를 대신해 리소스에 접근하는 애플리케이션

    • 클라이언트 ID와 클라이언트 시크릿으로 신원을 증명한다.

      • 사용자 자격 증명과는 다르다.

      • 클라이언트는 요청할 때 자신을 증명하는 자체 자격 증명이 필요하다.

  • 권한 부여 서버

    • 리소스 서버가 노출하는 리소스에 접근할 권한을 부여하는 애플리케이션

    • 클라이언트가 접근 권한이 있다고 결정하면 토큰을 발급한다.

    • 유효한 토큰으로 클라이언트가 접근하면 허용한다.

12.3. OAuth 2를 구현하는 방법 선택

  • OAuth 2를 이용한다 = 권한 부여에 토큰을 부여한다.

  • 토큰을 얻는 방법을 그랜트라고 하며 다양한 종류가 있다.

    • 승인 코드

    • 암호

    • 클라이언트 자격 증명

    • 갱신 토큰

12.3.1. 승인 코드 그랜트 유형의 구현

  • 가장 많이 이용되는 흐름 중 하나

  1. 인증을 요청한다.

  2. 액세스 토큰을 얻는다.

  3. 보호된 리소스를 호출한다.

1단계: 인증 요청 수행

  • 클라이언트가 사용자가 인증해야 하는 권한 부여 서버의 엔드포인트로 리디렉션 한다.

    • 사용자가 권한 부여 서버와 직접 상호 작용한다. 클라가 자격 증명을 보내는 것이 아니다.

  • 다음의 세부 정보가 포함된 쿼리로 권한 부여 엔드포인트를 호출한다.

    • response_type

      • 코드를 기대한다는 것을 알리는 값인 code를 포함한다.

    • client_id

      • 애플리케이션 자체를 식별하는 클라이언트 ID 값

    • redirect_uri

      • 인증 성공 후 사용자를 리디렉션할 위치

      • 권한 부여 서버가 이미 알고 있는 경우엔 보낼 필요가 없다.

    • scope

      • 허가된 권한

    • state

      • CSRF 보호를 위한 토큰을 정의한다.

2단계: 액세스 토큰 얻기

  • 1단계에서 생성한 코드는 리소스에 접근할 수 있게 사용자가 인증했다는 클라이언트의 증명이다.

  • 이제 이 코드로 권한 부여 서버를 호출한다.

    • 1단계와 달리 클라와 권한 부여 서버 간에 상호 작용이 일어난다.

  • 다음의 세부 정보를 포함한다.

    • code

      • 1단계에서 받은 승인 코드

      • 사용자가 인증받았음을 증명한다.

    • client id, client secret

      • 클라이언트의 자격 증명

    • redirect_uri

      • 1단계와 같다.

    • grant_type

      • 흐름의 유형을 식별한다.

      • authorization_code 값을 가진다.

  • 이에 대한 응답으로 access_token을 반환한다.

3단계: 보호된 리소스 호출

  • 클라이언트는 리소스 서버의 엔드포인트를 호출할 때 권한 부여 요청 헤더의 액세스 토큰을 사용한다.

12.3.2. 암호 그랜트 유형 구현

  • 리소스 소유자 자격 증명 그랜트 유형이라고도 한다.

  • 클라와 권한 부여 서버가를 같은 조직에서 구축하고 관리할 때만 이용한다.

  • 사용자 자격 증명을 클라이언트와 공유한다고 가정하기 때문에 승인 코드 부여 유형보다 덜 안전하다.

    • 실제 시나리오에서 피하는 것이 좋다.

  1. 액세스 토큰을 요청한다.

  2. 액세스 토큰을 이용해 리소스를 호출한다.

1단계: 액세스 토큰 요청

  • 클라가 사용자 자격 증명을 수집하고 권한 서버를 호출해 액세스 토큰을 얻는다.

  • 다음의 정보를 함께 보낸다.

    • grant_type

      • password를 가진다.

    • clientid, client_secret

      • 클라이언트가 자신을 인증하기 위한 자격 증명

    • scope

      • 허가된 권한

    • username, password

      • 사용자 자격 증명

      • 일반 텍스트 형식으로 요청 헤더에 담아 전송된다.

2단계: 액세스 토큰을 이용해 리소스 호출

  • 권한 부여 요청 헤더에 액세스 토큰을 추가한다.

12.3.3. 클라이언트 자격 증명 그랜트 유형 구현

  • 가장 단순한 그랜트 유형

  • 사용자가 관여하지 않을 떄, 즉 두 애플리케이션 간 인증을 구현할 떄 이용한다.

  • 액세스 토큰을 요청할 떄 자격 증명이 필요하지 않다.

  1. 액세스 토큰을 요청한다.

  2. 액세스 토큰을 이용해 리소스를 호출한다.

1단계: 액세스 토큰 얻기

  • 클라는 액세스 토큰을 얻기 위해 다음의 정보를 함께 보낸다.

    • grant_type

      • client_credentials 값을 가진다.

    • client_id, client_secret

      • 클라이언트 자격 증명을 나타낸다.

    • scope

      • 허가된 권한을 나타낸다.

2단계: 액세스 토큰을 이용해 리소스 호출

  • 얻은 토큰으로 리소스 서버의 엔드포인트를 호출한다.

  • 마찬가지로 권한 부여 요청 헤더에 액세스 토큰을 추가한다.

12.3.4. 갱신 토큰으로 새 액세스 토큰 얻기

  • 갱신 토큰은 액세스 토큰을 반환할 때 함꼐 반환한다.

  • 토큰은 보안을 위해 최소한의 수명을 가지도록 해야한다.

    • 새 액세스 토큰을 얻기 위해 갱신 토큰을 이용하면 재인증을 하지 않아도 된다.

    • 자격 증명을 안전하지 않게 저장하거나 매번 사용자를 리디렉션 할 필요가 없다.

  • 토큰이 노출되면 취소할 수 있어 안전하다.

12.4. OAuth 2의 허점

  • CSRF 이용

    • CSRF 보호를 적용하지 않으면 위조 요청이 가능하다.

  • 클라이언트 자격 증명 도용

    • 자격 증명을 저장하거나 전송하면 도용할 수 있다.

  • 토큰 재생

    • 누군가가 네트워크를 보내는 동안 가로챌 수 있다.

  • 토큰 하이재킹

    • 인증 프로세스를 방해하고 토큰을 훔치는 행위를 말한다.

12.5. 간단한 SSO(Single Sign On) 애플리케이션 구현

  • 사용자가 권한 부여 서버를 통해 인증하면 앱이 갱신 토큰을 이용해 로그인 상태를 유지한다.


@Configuration
public class ProjectConfig {

    ...

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.oauth2Login(httpSecurityOAuth2LoginConfigurer ->
                httpSecurityOAuth2LoginConfigurer.clientRegistrationRepository(clientRegistrationRepository()));

        return http.build();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        ClientRegistration clientRegistration = clientRegistration();
        return new InMemoryClientRegistrationRepository(clientRegistration);
    }

    private ClientRegistration clientRegistration() {
        return CommonOAuth2Provider.GITHUB.getBuilder("github").build();
    }
}
  • oauth2Login()을 설정하면 필터 체인에 OAuth2LoginAuthenticationFilter를 추가한다.

Last updated

Was this helpful?