이 글에서 다루는 내용

간편로그인 개발에서 접하게 되는 용어들을 정리했습니다

OAuth 2.0

OAuth 2.0은 권한 위임(Authorization) 프로토콜입니다.

"이 사용자가 특정 리소스에 접근해도 됩니까?" 라는 질문에 답하는 것이 목적입니다. 사용자 인증(누구인지)이 아닌, 권한(무엇을 할 수 있는지)에 초점을 맞춥니다.

응답 형식

{
  "access_token": "ya29.a0AfH6SMBx...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "refresh_token": "1//0gLzq...",
  "scope": "email profile"
}

access_token은 불투명한 문자열(Opaque Token)로, 내부에 사용자 정보를 담고 있지 않습니다. 사용자 정보를 얻으려면 토큰을 가지고 별도의 API를 호출해야 합니다.

핵심 개념

개념 설명
scope 어떤 권한을 허가했는지
access_token 리소스 접근에 사용하는 토큰
refresh_token access_token 만료 시 재발급용
expires_in 토큰 유효 시간 (초)

OAuth 2.0은 사용자가 누구인지 관심이 없습니다. 어떤 리소스에 접근 가능한지만 다룹니다.

OIDC (OpenID Connect)

OIDC는 OAuth 2.0 위에 사용자 인증(Authentication) 레이어를 추가한 프로토콜입니다.

OAuth 2.0
└── OIDC (OAuth 2.0을 확장한 인증 프로토콜)

OAuth 2.0을 대체하는 게 아니라, OAuth 2.0 흐름을 그대로 사용하면서 ID Token 발급을 추가한 것입니다.

OAuth 2.0과의 차이

OAuth 2.0 OIDC
목적 권한 위임 사용자 인증
핵심 질문 "무엇을 할 수 있는가?" "누구인가?"
발급 토큰 Access Token Access Token + ID Token
사용자 정보 별도 API 호출 필요 ID Token에 포함

OIDC 응답 형식

{
  "access_token": "ya29.a0AfH6SMBx...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "refresh_token": "1//0gLzq...",
  "scope": "email profile",

  "id_token": "eyJhbGci..."
}

OAuth 2.0 응답과 동일하고 id_token 필드 하나만 추가됩니다.

OIDC가 추가한 것들

ID Token — 사용자 정보를 담은 JWT 형식의 토큰

UserInfo 엔드포인트 — Access Token으로 추가 사용자 정보 조회

GET https://openid.googleapis.com/v1/userinfo
Authorization: Bearer {access_token}

Discovery 문서 — JWK URI, 지원 알고리즘 등 메타정보를 자동으로 제공

GET https://accounts.google.com/.well-known/openid-configuration

구글 로그인은 OIDC 프로토콜 위에서 동작합니다. Google 간편로그인 SDK에서 받는 ID Token이 바로 OIDC가 발급하는 토큰입니다.

JWT (JSON Web Token)

JWT는 데이터를 안전하게 전달하기 위한 토큰 형식입니다. 토큰 자체에 정보를 담고 있어서 별도의 DB 조회 없이 검증이 가능합니다.

구조

JWT는 .으로 구분된 3개의 파트로 구성됩니다.

header.payload.signature

eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTYifQ.서명값

Header — 어떤 알고리즘으로 서명했는지

{
  "alg": "RS256",
  "kid": "key-1",
  "typ": "JWT"
}

Payload — 실제 데이터 (Claims)

{
  "iss": "https://accounts.google.com",
  "aud": "our-client-id",
  "sub": "109876543210",
  "exp": 1711234567,
  "iat": 1711230967,
  "email": "user@gmail.com",
  "email_verified": true,
  "name": "홍길동",
  "picture": "https://...",
  "given_name": "길동",
  "family_name": "홍"
}

Signature — 위변조 방지를 위한 서명값

RSA256(
  base64(header) + "." + base64(payload),
  개인키
)

검증 항목

항목 검증 내용 목적
signature 공개키로 서명 검증 위변조 확인
iss 발급자가 신뢰할 수 있는지 출처 확인
aud 우리 client-id와 일치하는지 다른 앱 토큰 도용 방지
exp 현재 시간보다 미래인지 만료 토큰 차단

aud 검증이 중요한 이유

A앱 해커가 구글 로그인 → A앱의 ID Token 획득
              ↓
우리 서버에 그 토큰으로 로그인 시도
              ↓
aud 검증 → A앱 client-id != 우리 client-id ❌ 차단

aud 검증이 없으면 다른 앱에서 발급된 토큰으로도 우리 서버에 로그인할 수 있습니다.

ID Token은 JWT 형식을 사용한 구현체입니다. JWT는 형식(Format)이고, ID Token은 그 형식을 활용한 인증 토큰입니다.

JWK (JSON Web Key)

JWK는 암호화 키를 JSON 형식으로 표현한 표준입니다. JWT의 서명을 검증할 때 사용하는 공개키를 나타냅니다.

형식

{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "kid": "key-1",
      "n": "공개키 값...",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "kid": "key-2",
      "n": "공개키 값...",
      "e": "AQAB"
    }
  ]
}

구글은 https://www.googleapis.com/oauth2/v3/certs 에 공개키를 공개해놓습니다.

JWT와의 관계

구글 서버:  개인키(Private Key)로 ID Token(JWT)에 서명
우리 서버:  공개키(JWK)로 JWT 서명 검증

JWK가 여러 개인 이유

구글은 보안을 위해 주기적으로 키를 교체(Key Rotation) 합니다.

키 교체 전:  key-1 으로 서명 → key-1 으로 검증 ✅
키 교체 중:  key-2 로 서명   → key-1, key-2 둘 다 공개 (중복 기간)
키 교체 후:  key-1 제거      → key-2 로만 검증

JWT의 Header에 kid (Key ID) 필드가 있어서, 어떤 키로 서명됐는지 명시합니다.

JWT Header: { "alg": "RS256", "kid": "key-1" }
                                      ↓
            JWK 목록에서 kid가 "key-1"인 공개키로 검증

client-secret이 필요 없는 이유

대칭키 방식 비대칭키 방식 (JWK)
secret을 양쪽이 공유 공개키는 누구나 가져올 수 있음
secret 노출 위험 개인키는 구글만 보유

공개키는 누구나 가져올 수 있으므로, 서버에 client-secret 없이도 검증이 가능합니다.

전체 관계 정리

OAuth 2.0  →  권한 위임 프로토콜 (기반)
   └── OIDC  →  인증 추가 (ID Token 발급)
                    └── ID Token  →  JWT 형식 사용
                                         └── 서명 검증  →  JWK (공개키) 사용
용어 한 줄 요약
OAuth 2.0 권한을 위임하는 프로토콜
OIDC OAuth 2.0에 사용자 인증을 추가한 프로토콜
JWT 데이터를 담는 토큰 형식
JWK JWT 서명을 검증하는 공개키 형식