OAuth 2.0
OAuth 2.0은 사용자가 자신의 비밀번호를 제3자 애플리케이션에 직접 알려주지 않고도 특정 리소스에 대한 접근 권한을 위임할 수 있게 해주는 인가(Authorization) 프레임워크입니다. 구글 계정으로 다른 앱에 로그인하거나, GitHub 계정으로 CI 도구에 저장소 접근 권한을 줄 때 뒤에서 동작하는 프로토콜이 OAuth입니다. Resource Owner(사용자), Client(제3자 앱), Authorization Server(인가 서버), Resource Server(보호된 리소스)라는 네 가지 역할을 정의하고, 이들 사이에서 인가 코드와 액세스 토큰이 교환되는 흐름을 규격화합니다. 인증(Authentication)이 아닌 인가(Authorization)에 초점을 맞추고 있으며, 인증까지 표준화한 것이 OAuth 위에 얹어진 OpenID Connect입니다.
▶아키텍처 다이어그램
🔄 프로세스 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
어떤 앱이 사용자의 구글 캘린더 일정을 읽어야 한다고 가정합니다. 가장 단순한 방법은 사용자에게 구글 비밀번호를 달라고 하는 것입니다. 하지만 이렇게 하면 그 앱은 캘린더뿐 아니라 이메일, 드라이브, 결제 정보까지 전부 볼 수 있게 됩니다. 사용자는 한 앱을 믿을 수 없게 됐을 때 비밀번호를 바꿔야 하고, 그러면 같은 비밀번호를 쓰던 다른 앱도 전부 끊깁니다. 비밀번호를 직접 넘기는 방식은 권한을 좁힐 수 없고, 회수할 수 없고, 추적할 수 없다는 세 가지 문제를 동시에 안고 있습니다. 제3자에게 내 리소스 중 일부에만, 정해진 기간 동안만, 언제든 철회 가능한 형태로 접근을 허용할 방법이 필요했습니다.
OAuth 이전에도 서비스 간 인증 연동은 있었지만 표준이 없었습니다. 각 플랫폼이 독자적인 토큰 발급 방식을 만들었고, 제3자 앱 개발자는 서비스마다 다른 인증 흐름을 따로 구현해야 했습니다. 2007년 트위터, 구글, 야후 등의 엔지니어들이 모여 OAuth 1.0을 만들었지만, 암호학적 서명 요구 사항이 복잡해서 구현 난이도가 높았습니다. 2012년 RFC 6749로 발표된 OAuth 2.0은 이 복잡성을 줄이는 대신 TLS를 필수로 요구하는 방향을 택했습니다. 서명 대신 토큰과 HTTPS에 보안을 위임함으로써 구현이 단순해졌고, 모바일 앱과 SPA 같은 새로운 클라이언트 유형에도 대응할 수 있게 확장됐습니다. 지금은 구글, GitHub, 카카오, 네이버 등 대부분의 대형 플랫폼이 OAuth 2.0을 기반으로 API 접근 권한을 관리합니다.
OAuth 2.0에서 가장 많이 쓰이는 Authorization Code Grant 플로우를 따라가 보겠습니다. 사용자가 제3자 앱에서 '구글로 로그인' 버튼을 누르면, 앱은 사용자를 구글의 인가 서버로 리다이렉트합니다. 이때 어떤 권한을 요청하는지(scope), 응답을 어디로 받을지(redirect_uri)를 URL 파라미터로 보냅니다. 구글 인가 서버는 사용자에게 동의 화면을 보여줍니다. '이 앱이 당신의 캘린더 일정을 읽으려 합니다. 허용하시겠습니까?' 사용자가 동의하면, 인가 서버는 일회용 인가 코드를 redirect_uri로 돌려보냅니다. 제3자 앱의 백엔드 서버는 이 인가 코드를 받아서 인가 서버에 다시 보내며 액세스 토큰으로 교환합니다. 이 교환 과정은 백엔드에서 일어나므로 토큰이 브라우저에 노출되지 않습니다. 앱은 받은 액세스 토큰을 API 요청의 Authorization 헤더에 넣어 리소스 서버에 보냅니다. 리소스 서버는 토큰의 유효성과 scope를 확인한 뒤, 허용된 범위의 데이터만 응답합니다. 인가 코드를 중간에 가로채는 공격을 막기 위해 PKCE라는 추가 검증이 있습니다. 클라이언트가 랜덤 값의 해시를 인가 요청에 포함하고, 토큰 교환 시 원본 값을 보내 대조하는 방식입니다. 퍼블릭 클라이언트(SPA, 모바일 앱)에서는 PKCE가 필수입니다.
OAuth와 가장 자주 혼동되는 것은 인증(Authentication)과 인가(Authorization)의 차이입니다. OAuth 2.0은 인가 프레임워크입니다. '이 앱이 내 캘린더를 읽어도 되는가'를 다루지, '이 사람이 정말 홍길동인가'를 다루지는 않습니다. 액세스 토큰은 권한 증명이지 신원 증명이 아닙니다. '구글로 로그인'처럼 신원 확인까지 하려면 OAuth 위에 OpenID Connect를 얹어서 ID Token을 받아야 합니다. JWT와의 관계도 자주 헷갈리는 지점입니다. OAuth는 토큰을 주고받는 절차를 정의한 프레임워크이고, JWT는 그 토큰의 형식 중 하나입니다. OAuth의 액세스 토큰이 반드시 JWT일 필요는 없지만, 실무에서는 JWT 형식을 쓰는 경우가 많습니다. OAuth는 '누가 누구에게 어떤 권한을 어떻게 위임하는가'의 절차를 정하고, JWT는 '그 권한 정보를 어떤 구조의 토큰에 담는가'를 정합니다. API Key와도 비교할 수 있습니다. API Key는 발급받은 키를 직접 요청에 넣는 단순한 방식이지만 사용자별 권한 분리나 동의 절차가 없고, 키가 유출되면 전체 접근이 뚫립니다. 사용자 동의 기반의 세밀한 권한 위임이 필요한 상황에서는 OAuth가 적합합니다.
OAuth 2.0은 소셜 로그인이 가장 눈에 띄는 사용처이지만, 실제로는 서비스 간 API 접근 제어의 표준 역할을 합니다. 제3자 앱이 사용자의 데이터에 접근해야 하는 상황이라면 OAuth가 자연스러운 선택입니다. CI/CD 도구가 GitHub 저장소에 접근하거나, 분석 도구가 구글 애널리틱스 데이터를 읽거나, 슬랙 봇이 채널에 메시지를 보내는 경우가 모두 OAuth 플로우를 거칩니다. 내부 마이크로서비스 간에도 쓰입니다. 사용자가 관여하지 않는 서버 대 서버 통신에서는 Client Credentials Grant를 사용해 서비스 자체의 자격 증명으로 토큰을 발급받습니다. 구현할 때 주의할 점은 scope를 최소한으로 요청하는 것입니다. 캘린더 읽기만 필요한데 전체 계정 접근을 요구하면 사용자 신뢰를 잃고, 토큰이 유출됐을 때 피해 범위도 커집니다. redirect_uri를 정확히 검증하지 않으면 인가 코드가 공격자에게 넘어갈 수 있으므로, 등록된 URI와 정확히 일치하는지 확인해야 합니다.