OAuth 2.0: 개념부터 실무까지 완벽 정리
1. OAuth란 무엇인가? (아파트 방문증 비유)
OAuth의 탄생 배경
과거에는 다른 서비스와 연동하려면 사용자의 아이디와 비밀번호를 직접 넘겨줘야 했습니다. 예를 들어, 사진 인쇄 서비스가 Google Drive에 접근하려면 사용자의 Google 비밀번호를 받아야 했죠. 이는 보안적으로 매우 위험한 방식입니다.
OAuth는 이 문제를 해결합니다. "비밀번호를 넘기지 않고도, 제한된 권한만 안전하게 위임하는 표준"입니다.
"아파트 방문증" 비유
OAuth를 이해하는 가장 쉬운 방법은 아파트 방문증에 비유하는 것입니다.
- 아파트 주인 (Resource Owner) = 사용자. 내 데이터의 주인입니다.
- 택배 기사 (Client) = 제3자 앱. 주인의 데이터에 접근하고 싶어합니다.
- 관리실 (Authorization Server) = Google, Kakao 등 인증 서버. 방문증을 발급합니다.
- 아파트 건물 (Resource Server) = 데이터가 저장된 서버.
택배 기사에게 집 열쇠(비밀번호)를 주는 대신, 관리실에서 방문증(Access Token)을 발급합니다. 이 방문증으로는 현관 앞까지만 갈 수 있고, 방 안에 들어가거나 물건을 가져갈 수는 없습니다. 유효 시간도 정해져 있죠.
2. OAuth 1.0 vs OAuth 2.0
| 구분 | OAuth 1.0 | OAuth 2.0 |
|---|---|---|
| 보안 방식 | 복잡한 서명(Signature) 계산 | HTTPS 의존 + Bearer Token |
| 구현 난이도 | 매우 어려움 | 상대적으로 간단 |
| 토큰 형태 | 고정된 단일 토큰 | Access Token + Refresh Token |
| 유연성 | 웹 전용 | 웹, 모바일, IoT 등 다양한 환경 지원 |
| 현재 사용 | 거의 사용하지 않음 | 사실상 표준 (현재 대부분 사용) |
OAuth 2.0은 OAuth 1.0의 복잡성을 줄이고, 다양한 플랫폼에서 사용할 수 있도록 설계된 완전히 새로운 프로토콜입니다.
3. OAuth 2.0의 4가지 역할
1) Resource Owner (리소스 소유자)
- 누구?: 데이터의 진짜 주인 — 즉, 사용자(당신)입니다.
- 역할: 제3자 앱에게 자신의 데이터 접근을 허용하거나 거부합니다.
- 예시: "이 앱이 내 Google 프로필을 볼 수 있게 허용할까?"를 결정하는 사람.
2) Client (클라이언트)
- 누구?: 사용자의 데이터에 접근하려는 제3자 앱입니다.
- 역할: 사용자 대신 리소스 서버에서 데이터를 가져옵니다.
- 주의: 여기서 "Client"는 브라우저가 아니라, 제3자 서비스(앱)를 의미합니다.
- 예시: "Google로 로그인" 기능이 있는 쇼핑몰 앱, Notion, Figma 등.
3) Authorization Server (인증 서버)
- 누구?: 사용자를 인증하고, 토큰을 발급하는 신뢰할 수 있는 서버입니다.
- 역할: 로그인 처리, 권한 동의 확인, Authorization Code 및 Access Token 발급.
- 예시: Google의
accounts.google.com, Kakao의kauth.kakao.com.
4) Resource Server (리소스 서버)
- 누구?: 사용자의 실제 데이터를 저장하고 있는 API 서버입니다.
- 역할: 유효한 Access Token이 있는 요청에 대해서만 데이터를 제공합니다.
- 예시: Google People API, Kakao 사용자 정보 API.
핵심: Authorization Server와 Resource Server는 같은 회사(예: Google)의 서버이지만, 역할이 다릅니다. 하나는 "문지기", 다른 하나는 "창고지기"입니다.
4. Authorization Code Grant 흐름 상세
가장 안전하고 가장 많이 사용되는 방식입니다. 위 시뮬레이션에서 체험한 흐름을 텍스트로 정리합니다.
- 사용자가 "Google로 로그인" 클릭
- 클라이언트 앱이
client_id,redirect_uri,scope,state를 포함한 URL을 만들어 인증 서버로 리다이렉트합니다.
- 클라이언트 앱이
- 인증 서버에서 로그인 & 권한 동의
- 사용자가 Google 계정으로 로그인하고, 앱이 요청한 권한(프로필, 이메일 등)을 확인 후 허용합니다.
- Authorization Code 발급
- 인증 서버가
redirect_uri로 일회용 Authorization Code를 전달합니다. (브라우저 URL에 포함)
- 인증 서버가
- Code를 Token으로 교환 (백엔드)
- 클라이언트의 백엔드 서버가 Authorization Code +
client_secret을 인증 서버에 보내 Access Token과 Refresh Token을 받습니다.
- 클라이언트의 백엔드 서버가 Authorization Code +
- Access Token으로 API 호출
Authorization: Bearer {access_token}헤더를 붙여 리소스 서버에 요청합니다.
- 데이터 반환 & 로그인 완료
- 리소스 서버가 사용자 정보를 반환하고, 클라이언트 앱이 이를 기반으로 회원가입/로그인을 처리합니다.
5. 다른 Grant Types
1) Implicit Grant (암묵적 승인)
- 특징: Authorization Code 없이 Access Token을 직접 브라우저에 반환합니다.
- 장점: 단순하고 빠릅니다.
- 단점: 토큰이 URL fragment에 노출되어 보안이 취약합니다.
- 현재: 사용을 권장하지 않습니다. PKCE를 적용한 Authorization Code Grant를 대신 사용하세요.
2) Client Credentials Grant (클라이언트 자격증명)
- 특징: 사용자 개입 없이, 앱 자체가 인증합니다.
- 사용처: 서버-to-서버 통신, 마이크로서비스 간 인증.
- 예시: 백엔드 서비스가 다른 백엔드 API에 접근할 때.
3) Resource Owner Password Credentials (비밀번호 방식)
- 특징: 사용자가 앱에 직접 아이디/비밀번호를 입력합니다.
- 단점: OAuth의 핵심 원칙("비밀번호를 넘기지 않는다")에 위배됩니다.
- 현재: 보안상 사용을 권장하지 않습니다.
4) Device Authorization Grant (디바이스 인증)
- 특징: 키보드가 없는 기기(스마트 TV, IoT)에서 사용합니다.
- 흐름: 기기에 코드가 표시되고, 사용자가 스마트폰/PC에서 해당 코드를 입력하여 인증합니다.
6. Access Token과 Refresh Token
| 구분 | Access Token | Refresh Token |
|---|---|---|
| 비유 | 출입증 (방문 카드) | 재발급 요청서 |
| 용도 | API 요청 시 인증 수단 | Access Token 만료 시 새로 발급받기 위한 용도 |
| 유효기간 | 짧음 (보통 1시간 이내) | 김 (보통 2주~수개월) |
| 저장 위치 | 메모리 또는 안전한 저장소 | HttpOnly Cookie 또는 서버 DB |
| 탈취 시 위험도 | 낮음 (곧 만료됨) | 높음 (장기간 사용 가능) |
Token Refresh 흐름:
- Access Token이 만료됩니다. (API가
401 Unauthorized반환) - 클라이언트가 Refresh Token을 인증 서버에 보냅니다.
- 인증 서버가 새 Access Token (+ 경우에 따라 새 Refresh Token)을 발급합니다.
- 사용자는 다시 로그인할 필요 없이 서비스를 계속 이용합니다.
Tip: JWT 시뮬레이션에서 Access Token과 Refresh Token의 탈취 시나리오와 저장 전략을 더 자세히 체험할 수 있습니다.
7. OAuth와 보안
PKCE (Proof Key for Code Exchange)
모바일 앱이나 SPA(Single Page App)처럼 client_secret을 안전하게 보관하기 어려운 환경에서, Authorization Code를 더 안전하게 교환하기 위한 확장입니다.
- 앱이
code_verifier(랜덤 문자열)를 생성하고, 이를 해시한code_challenge를 인증 요청에 포함합니다. - 토큰 교환 시 원본
code_verifier를 보내 검증합니다. - 중간에 Authorization Code를 탈취해도,
code_verifier없이는 토큰 교환이 불가능합니다.
state 파라미터 (CSRF 방지)
- 클라이언트가 인증 요청 시 랜덤
state값을 포함합니다. - 콜백 시 같은
state값이 돌아오는지 확인합니다. - 공격자가 위조한 콜백을 보내더라도
state가 일치하지 않으면 거부됩니다.
redirect_uri 검증
- 인증 서버는 사전에 등록된
redirect_uri만 허용합니다. - 공격자가
redirect_uri를 자신의 서버로 바꿔치기하는 것을 방지합니다.
8. Scope — 권한의 범위를 정하는 방법
OAuth 2.0에서 scope(스코프)는 "이 앱에게 어디까지 허용할 것인가"를 정의합니다. 아파트 방문증 비유로 말하면, 방문증에 "현관 앞까지만 허용" 또는 "거실까지 허용"이라고 적혀 있는 것과 같습니다.
8-1. 실제 서비스의 scope 예시
| 서비스 | scope 예시 | 의미 |
|---|---|---|
profile, email |
이름/사진, 이메일 읽기 | |
drive.readonly |
Google Drive 파일 읽기만 (수정 불가) | |
| GitHub | read:user, repo |
프로필 읽기, 저장소 접근 |
admin:org |
조직 관리자 권한 | |
| Kakao | profile_nickname |
닉네임만 읽기 |
| Slack | channels:read, chat:write |
채널 목록 읽기, 메시지 전송 |
8-2. Scope는 어떻게 동작하나?
- 요청 시: 클라이언트 앱이 인증 요청에 필요한 scope를 명시합니다.
scope=profile email drive.readonly
- 동의 화면: 사용자가 해당 권한을 확인하고 허용/거부합니다.
- 토큰 발급: 허용된 scope가 Access Token의 payload에 포함됩니다.
{ "scope": "profile email", "exp": 1707400000 }
- API 요청 시: 리소스 서버가 토큰의 scope를 확인하여 접근을 허용/차단합니다.
8-3. 서버가 Scope를 검증하는 원리
토큰은 scope를 들고 다닐 뿐이고, 실제 차단/허용 판단은 리소스 서버의 책임입니다.
| API 요청 | 필요한 scope | 토큰의 scope | 결과 |
|---|---|---|---|
GET /userinfo |
profile |
profile email |
✅ 200 OK |
GET /drive/files |
drive.readonly |
profile email |
❌ 403 Forbidden |
DELETE /account |
admin |
profile email |
❌ 403 Forbidden |
scope 변경은 가능한가요?
발급된 토큰의 scope는 변경할 수 없습니다. 토큰은 서명(signature)으로 보호되어 있어 payload를 조작하면 서명 검증에서 거부됩니다. 권한을 변경하려면 OAuth 흐름을 다시 수행하여 새 토큰을 발급받아야 합니다.
9. JWT와의 관계
OAuth 2.0과 JWT는 자주 함께 언급되지만, 서로 다른 것입니다.
| 구분 | OAuth 2.0 | JWT |
|---|---|---|
| 본질 | 인증/인가 프레임워크 (절차) | 토큰 포맷 (형식) |
| 비유 | "방문증을 발급하는 절차" | "방문증의 규격 (재질, 크기)" |
| 역할 | "누가, 어떤 앱에, 어떤 권한을 줄 것인가"를 결정 | "토큰 안에 어떤 정보를, 어떤 형식으로 담을 것인가"를 정의 |
OAuth 2.0의 Access Token은 JWT 형식일 수도 있고, 단순한 랜덤 문자열(Opaque Token)일 수도 있습니다. JWT를 사용하면 토큰 자체에 사용자 정보가 포함되어 리소스 서버가 별도의 검증 요청 없이 토큰을 확인할 수 있다는 장점이 있습니다.
10. 실무 활용
소셜 로그인
- Google, Kakao, Naver, Apple 등의 "소셜 로그인"은 OAuth 2.0 기반입니다.
- 사용자 입장: 비밀번호를 새로 만들 필요 없이 기존 계정으로 편리하게 로그인.
- 개발자 입장: 비밀번호 관리 부담 없이 안전한 인증 구현.
API 인증
- GitHub API, Google APIs, Slack API 등 거의 모든 주요 API가 OAuth 2.0을 사용합니다.
- 서드파티 앱이 사용자를 대신해 API를 호출할 때 Access Token으로 인증합니다.
마이크로서비스
- 서비스 간 인증에 Client Credentials Grant를 사용합니다.
- API Gateway에서 토큰 검증을 중앙화하여 각 서비스의 부담을 줄입니다.
11. 자주 묻는 질문 (FAQ)
Q. OAuth와 로그인은 같은 건가요?
아닙니다. OAuth는 "인가(Authorization)"를 위한 프로토콜입니다. "이 앱이 내 데이터에 접근해도 되는가?"를 결정합니다. 로그인(인증, Authentication)은 "이 사람이 누구인가?"를 확인하는 것입니다. 소셜 로그인에서는 OAuth의 결과(사용자 정보)를 이용해 로그인을 처리하지만, OAuth 자체의 목적은 인가입니다.
Q. 왜 Authorization Code를 거치나요? 바로 토큰을 주면 안 되나요?
리다이렉트는 브라우저(프론트엔드)를 거치기 때문에 URL에 포함된 정보가 노출될 수 있습니다. Authorization Code는 일회용이고 짧은 유효기간을 가지며, client_secret 없이는 토큰으로 교환할 수 없습니다. 즉, 코드가 유출되어도 안전합니다.
Q. client_secret은 어디에 보관하나요?
절대로 프론트엔드(브라우저/앱)에 넣지 않습니다. 백엔드 서버의 환경 변수나 보안 저장소에 보관하고, 서버 간 통신에서만 사용합니다.
Q. Access Token이 탈취되면 어떻게 하나요?
- Access Token의 유효기간을 짧게 설정합니다 (15분~1시간).
- Refresh Token으로 새 Access Token을 발급받는 구조를 사용합니다.
- 심각한 경우, 인증 서버에서 해당 사용자의 모든 토큰을 무효화(revoke)합니다.
Q. OpenID Connect(OIDC)는 뭔가요?
OAuth 2.0 위에 인증(Authentication) 레이어를 추가한 프로토콜입니다. OAuth 2.0이 "권한 위임"에 초점을 맞춘다면, OIDC는 "이 사람이 누구인가"를 확인하는 ID Token을 추가로 제공합니다. Google의 "Sign in with Google"은 OIDC 기반입니다.