OAuth 2.0은 오늘날 웹 애플리케이션에서 가장 널리 사용되는 인증 및 인가 프로토콜 중 하나입니다. 주로 제3자 애플리케이션이 사용자 자원에 접근할 수 있도록 안전하게 허용하는 데 사용됩니다. 이번 글에서는 OAuth 2.0을 이용한 인증 구현에 대해 구체적으로 살펴보겠습니다.

OAuth 2.0의 기본 개념

OAuth 2.0은 다양한 주체가 참여하는 인증 흐름으로, 주로 다음과 같은 역할이 있습니다:

  • 사용자(User): 인증을 요청하는 주체.
  • 클라이언트(Client): 사용자의 정보를 요청하는 애플리케이션.
  • 리소스 서버(Resource Server): 보호된 자원(예: 사용자 데이터)을 제공하는 서버.
  • 인증 서버(Authorization Server): 사용자 인증을 담당하고, 클라이언트에게 접근 토큰을 발급하는 서버.

이 프로토콜은 Access Token을 이용하여 클라이언트가 보호된 자원에 접근할 수 있도록 합니다. Access Token은 인증 서버가 클라이언트에게 발급하며, 이 토큰을 통해 리소스 서버는 클라이언트의 접근을 허가합니다.

구현 단계

1. 인증 서버 세팅

OAuth 2.0을 구현하기 위해 우선 인증 서버가 필요합니다. 일반적으로 OIDC(OpenID Connect)와 함께 사용되는 OAuth 2.0 라이브러리를 활용할 수 있습니다. 여기서는 Spring Security OAuth2를 사용하는 예를 들어보겠습니다.

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client-id")
            .secret("{noop}client-secret")
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .scopes("read", "write")
            .autoApprove(true);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .authorizationCodeServices(authorizationCodeServices())
            .authenticationManager(authenticationManager);
    }
}

위의 코드는 인메모리 클라이언트 설정을 보여줍니다. 클라이언트 ID와 비밀번호를 입력하여 애플리케이션이 인증 서버에 접근할 수 있도록 설정합니다.

2. 클라이언트 애플리케이션 설정

클라이언트 애플리케이션은 사용자에게 인증을 요청하고, 토큰을 받아와야 합니다. 아래는 간단한 요청 흐름을 나타낸 코드입니다.

function getAccessToken() {
    const clientId = 'client-id';
    const redirectUri = 'http://localhost:3000/callback';
    const authUrl = http://localhost:8080/oauth/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri};

    window.location.href = authUrl;
}

이 코드에서는 사용자가 인증 서버에 리다이렉트되도록 하는 URL을 생성합니다. 사용자가 인증을 완료하면, 리다이렉트 URI로 코드가 전달됩니다.

3. Access Token 요청

리다이렉트 URI에서 받은 코드를 사용하여 Access Token을 요청합니다.

async function fetchAccessToken(code) {
    const response = await fetch('http://localhost:8080/oauth/token', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            grant_type: 'authorization_code',
            code: code,
            redirect_uri: 'http://localhost:3000/callback',
            client_id: 'client-id',
            client_secret: 'client-secret',
        }),
    });

    const data = await response.json();
    return data.access_token;
}

여기서는 fetch를 사용하여 Access Token을 요청하는 예시를 보여주고 있습니다. 성공적으로 Access Token을 받으면, 이를 사용하여 보호된 자원에 접근할 수 있습니다.

4. 보호된 자원 접근

Access Token을 이용하여 보호된 자원에 접근합니다.

async function fetchProtectedResource(accessToken) {
    const response = await fetch('http://localhost:8080/api/protected', {
        method: 'GET',
        headers: {
            Authorization: Bearer ${accessToken},
        },
    });

    if (response.ok) {
        const data = await response.json();
        console.log(data);
    } else {
        console.error('Access denied:', response.status);
    }
}

Access Token을 Authorization 헤더에 포함하여 보호된 API에 접근하는 방법입니다.

마무리

OAuth 2.0을 통한 인증 구현은 매우 유용하며, 보안성을 높이는 방법 중 하나입니다. 사용자의 자원을 안전하게 보호하고, 제3자 애플리케이션에게 제한된 접근 권한을 부여할 수 있는 방법으로 널리 사용됩니다. 본 글에서는 간단한 예제를 통해 OAuth 2.0의 기본 개념과 실제 구현 절차를 살펴보았습니다. 다양한 라이브러리와 프레임워크를 통해 쉽게 구현할 수 있으니, 필요에 따라 적절한 도구를 선택하여 활용해 보시기 바랍니다.