JWT를 “인증 방식"이라 부르고, OAuth로 로그인했다고 말하고, SSO와 SAML을 같은 것으로 뭉뚱그리던 시절이 있었다. 용어가 죄다 인증 언저리에 있다 보니 머릿속에서 자꾸 섞였다. 한번 줄을 세워 정리해두고 싶었다.

기준은 두 가지 질문이다. 하나는 “누구인가"를 어떻게 증명하는가, 다른 하나는 그 증명을 stateless HTTP 요청마다 어떻게 이어붙이는가.

인증 vs 인가

가장 자주 섞이는 둘이다.

인증(authentication)은 “당신은 누구인가"를 확인한다. 사용자가 주장하는 신원이 진짜인지 검증하는 일이다. 인가(authorization)는 “당신은 무엇을 할 수 있는가"를 확인한다. 검증된 신원에게 특정 리소스나 동작에 대한 권한이 있는지 판단한다.

순서가 있다. 인증이 먼저고 인가가 다음이다. 로그인은 인증이고, 그 사용자가 관리자 페이지에 들어갈 수 있는지는 인가다. JWT를 “인증"이라 부르는 혼동도, OAuth를 “로그인"이라 부르는 혼동도 이 구분을 흐릿하게 본 데서 온다.

Basic · Digest · API Key

가장 단순한 출발점은 자격 증명 자체를 요청에 실어 보내는 것이다.

Basic 인증id:password를 Base64로 인코딩해 Authorization 헤더에 넣는다. Base64는 암호화가 아니라 인코딩이다. 누구나 디코딩해서 원문을 읽을 수 있으므로 HTTPS가 전제된다. 사람이 로그인하는 서비스보다는 서버 간 통신이나 내부 도구에서 여전히 보인다.

Digest 인증은 Base64 노출을 피하려고 나왔다. 서버가 던진 난수(nonce)에 비밀번호와 요청 정보를 더해 해시한 값을 보내므로, 평문 비밀번호가 네트워크에 흐르지 않는다. 원래 MD5 기반이고 이후 SHA-256이 추가됐지만(RFC 7616), 토큰과 TLS 조합에 밀려 지금은 거의 쓰지 않는다.

API Key는 클라이언트마다 무작위 문자열을 발급하고, 요청 시 헤더(X-API-KEY 등)에 실어 보낸다. 서버는 그 키를 저장소에서 조회해 누구의 키인지, 어떤 권한을 가졌는지 확인한다. 서드파티 연동이나 스크립트 호출에는 잘 맞는다. 다만 키 자체에 만료가 없어서, 유출되면 직접 폐기하거나 로테이션하기 전까지 계속 살아있다. 구조가 없는 문자열이라 매 요청마다 조회가 필요하다는 점도 부담이다.

이 방식들은 증명을 요청마다 다시 싣는다는 공통점이 있다. 상태를 따로 두지 않으니 단순하지만, 사람이 로그인하고 한참을 머무는 웹 서비스에는 부족하다. 그래서 한 번 증명한 사실을 이어가는 방식이 필요해진다.

Session과 Token

로그인은 한 번인데 요청은 계속 이어진다. 매번 비밀번호를 보낼 수는 없으니, “이미 증명됐다"는 사실을 어딘가 저장하고 이후 요청은 그 증표만 들고 온다. 저장 위치가 갈림길이다.

세션은 서버가 상태를 쥔다. 로그인하면 서버가 세션을 만들고 세션 ID만 쿠키로 내려준다. 서버가 직접 통제하니 강제 로그아웃 같은 즉시 무효화가 쉽고, 대신 서버가 상태를 기억해야 해서 수평 확장에 공유 저장소가 필요하다.

토큰(JWT, JSON Web Token)은 증표 안에 정보를 담아 클라이언트에 맡긴다. 서버는 서명만 검증하면 되므로 상태를 두지 않고, 분산 환경에서 확장이 자유롭다. 대신 한 번 발급된 토큰은 만료 전에 무효화하기 어렵다.

둘의 내부 구조, 저장 전략, access token과 refresh token 분리는 더 깊은 주제라 여기서는 생략한다.

OAuth 2.0과 OpenID Connect

여기서부터는 내 서버가 직접 인증하는 게 아니라, 남의 신원 공급자를 빌려 쓰는 영역이다. 그리고 가장 많이 섞이는 한 쌍이기도 하다.

OAuth 2.0(Open Authorization)은 인증이 아니라 인가 프레임워크다. 목적은 위임이다. 사용자가 자신의 비밀번호를 제3의 앱에 넘기지 않고도, 그 앱이 사용자 이름으로 Google Drive나 GitHub 리소스에 접근하도록 권한을 위임한다. 결과로 받는 건 리소스 접근용 access token이고, 이 토큰만으로는 사용자가 정확히 누구인지 알 수 없다.

OpenID Connect(OIDC)는 OAuth 2.0에 인증을 더한 계층이다. “Google로 로그인”, “GitHub로 로그인"이 바로 이것이다. OAuth의 access token에 더해, 사용자의 신원 정보를 담은 ID Token(JWT 포맷)을 함께 발급한다. 앱은 이 ID Token을 검증해 사용자가 누구인지 확정하고 로그인 세션을 만든다.

sequenceDiagram
    autonumber
    actor User as 사용자
    participant App as 애플리케이션
    participant IdP as 신원 공급자 (예: Google)

    User->>App: 'Google로 로그인' 클릭
    App->>IdP: 인증 요청 (동의 화면으로 이동)
    User->>IdP: 자격 증명 입력 및 권한 동의
    IdP-->>App: 인가 코드 발급
    App->>IdP: 인가 코드 ↔ 토큰 교환
    IdP-->>App: access token (인가) + ID Token (인증, JWT)
    Note over App: ID Token 을 검증해 사용자 신원 확정

정리하면 권한 위임이면 OAuth 2.0, 신원 확인이면 OIDC다. “OAuth로 로그인했다"는 말이 부정확한 이유가 여기 있다.

SSO

SSO(single sign-on, 단일 로그인)는 인증 “방식"이라기보다 경험에 가깝다. 한 번 로그인하면 연동된 여러 시스템을 다시 로그인하지 않고 쓴다. 이를 떠받치는 건 중앙 ID 공급자(Okta, Google Workspace 등)와, 시스템 사이에서 신원을 보증하는 ID 프로토콜이다.

대표적인 프로토콜이 SAML 2.0(Security Assertion Markup Language)과 OIDC다.

항목SAML 2.0OIDC
데이터 포맷XMLJSON / JWT
주 사용처엔터프라이즈, 레거시현대 웹·모바일, 클라우드

SAML 2.0은 XML 기반이라 상대적으로 무겁지만, 엔터프라이즈와 레거시 환경에서 오래 자리 잡았다. OIDC는 JSON/JWT 기반이라 가볍고 현대 웹·모바일 앱에 친화적이다. 앞 절에서 본 OIDC가 SSO 프로토콜로도 쓰인다는 점에서, 인증 위임과 SSO는 한 줄로 이어진다.

마무리

처음 혼동의 정체는 결국 두 가지를 흐릿하게 본 것이었다. 인증이냐 인가냐, 그리고 증명을 어디에 두느냐. Basic·Digest·API Key는 증명을 매번 다시 싣는다. Session과 Token은 한 번 증명한 사실을 이어가고, OAuth·OIDC·SSO는 그 증명을 아예 남에게 빌린다. 도구 이름을 외우는 것보다 지금 풀려는 문제가 어느 쪽인지 구분하는 게 먼저였다. 한 번 정리하고 나니 용어가 비로소 제자리를 찾았다.

참고

  • 세션 인증과 JWT — Session과 Token의 구조, 저장 전략, access/refresh 분리를 깊게 다룬다.