토큰이란?
불투명 토큰을 소개하기 전에, 토큰이 무엇인지 이해하는 것이 중요합니다:
토큰은 당사자 간의 안전한 정보를 나타내고 전송하는 데 사용되며, 인터넷에서 발생하는 대부분의 인증 (Authentication) 및 권한 부여 (Authorization) 프로세스를 지원합니다. 웹 서비스에서 가장 널리 사용되는 두 가지 유형의 토큰은 RFC 7519: JSON Web Tokens (JWT) 와 불투명 토큰입니다.
불투명 토큰 (Opaque token)이란?
불투명 토큰은 접근할 수 없는 독점적인 포맷의 토큰이며, 일반적으로 서버의 지속적인 저장소에 있는 정보에 대한 식별자를 포함합니다.
불투명 토큰은 토큰이 가질 수 있는 한 형태이며, 액세스 토큰 (Access token)과 갱신 토큰 (Refresh token)은 불투명 토큰으로 있을 수 있습니다. 불투명 토큰의 포맷은 issuer (발급자)에 의해 결정되며, 일반적으로 데이터베이스에서 특정 정보를 검색하고 식별하는 데 도움을 주기 위해 숫자 및/또는 문자로 구성된 문자열입니다. 이것은 불투명 토큰의 예입니다:
M-oxIny1RfaFbmjMX54L8Pl-KQEPeQvF6awzjWFA3iq
반면에 JWT는 또 다른 일반적인 토큰 포맷입니다. JWT는 모든 클레임 (claim)과 정보를 포함하며, issuer의 서명이 있는 JSON 문자열입니다. 기본적으로 암호화되어 있지 않지만, JSON Web Encryption (JWE) 표준을 사용하여 암호화할 수 있습니다. JWT는 일반적으로 암호화되어 있지 않지만, 서명의 존재는 토큰 내용의 무결성을 보장하므로 JWT 내부의 데이터에 대한 완전한 신뢰를 제공합니다.
JWT와 달리, 불투명 토큰은 보호된 리소스에서 바로 검증할 수 있는 모든 정보를 포함하고 있지 않으며, 대신 불투명 토큰의 issuer (보통 권한 부여 서버)에서의 검증이 필요합니다. 이 검증 프로세스는 일반적으로 토큰 인트로스펙션 (token introspection)이라고 합니다.
JWT란?
불투명 토큰과는 대조적으로, JWT는 구조화되고 읽을 수 있는 포맷으로 정보를 담고 있는 독립적인 상태의 토큰입니다.
JWT는 세 부분으로 구성됩니다: header
, payload
, 그리고 signature
, 각각 Base64URL로 인코딩되어 있습니다.
다음은 JWT의 예시입니다:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
header
는 토큰 유형과 서명에 사용된 알고리즘의 정보를 포함합니다. 예를 들어,{"alg": "HS256", "typ": "JWT"}
.payload
섹션은 사용자나 권한 부여에 대한 정보의 조각인 클레임을 포함합니다. 사용자 ID, 만료 시간, 스코프 등의 정보가 있는데, 이 데이터는 인코딩되어 있지만 암호화되지는 않아서 토큰을 가진 사람은 누구나 클레임을 볼 수 있지만, 서명을 무효화하지 않고는 변경할 수 없습니다. 사양 및 권한 부여 서버 구성에 따라 다양한 클레임을 페이로드에 포함할 수 있습니다. 이는 토큰의 독립적인 특성을 제공합니다. 예를 들어,{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
.signature
는 헤더, 페이로드, 그리고 지정된 알고리즘을 사용하는 비밀 키를 결합하여 생성됩니다. 이 서명은 토큰의 무결성을 확인하고 변조되지 않았음을 보장하는 데 사용됩니다.
JWT는 클라이언트나 어떤 서비스도 권한 부여 서버와 상호작용할 필요 없이 로컬에서 검증할 수 있기 때문에 일반적으로 사용됩니다. 이는 여러 서비스가 토큰의 진위를 독립적으로 확인해야 할 수 있는 분산 시스템에서 특히 효율적입니다.
그러나 이러한 편리함은 토큰의 클레임이 과도하게 노출되지 않도록 하는 책임도 수반합니다. 또한, JWT는 일반적으로 짧은 수명을 가지며, 토큰이 무기한 유효하지 않도록 하기 위해 만료 시간이 클레임에 포함됩니다.
불투명 액세스 토큰 (Opaque access token) 검증
불투명 액세스 토큰은 검증을 위해 권한 부여 서버로 다시 보내져야 합니다. 권한 부여 서버는 발급된 토큰의 상태를 유지하고 내부 저장소를 기반으로 토큰의 유효성을 결정합니다.
- 클라이언트가 권한 부여 서버에 액세스 토큰을 요청합니다.
- 권한 부여 서버가 불투명 토큰을 발급합니다.
- 클라이언트가 불투명 토큰을 헤더에 포함하여 리소스 접근 요청을 보냅니다.
- 리소스 제공자가 권한 부여 서버에 토큰 인트로스펙션 ( RFC 7662: OAuth 2.0 Token Introspection ) 요청을 보내어 토큰을 검증합니다.
- 권한 부여 서버가 토큰 정보를 응답합니다.
JWT 액세스 토큰 검증 (오프라인)
JWT 액세스 토큰은 클라이언트 또는 토큰의 공개 키에 접근할 수 있는 서비스에 의해 오프라인으로 검증될 수 있습니다.
- 리소스 제공자가 권한 부여 서버의 공개 키를 OIDC 검색 엔드포인트에서 미리 가져옵니다. 이 공개 키는 토큰의 서명을 검증하고 무결성을 보장하는 데 사용됩니다.
- 클라이언트가 권한 부여 서버에 액세스 토큰을 요청합니다.
- 권한 부여 서버가 JWT 토큰을 발급합니다.
- 클라이언트가 JWT 토큰을 헤더에 포함하여 리소스 접근 요청을 보냅니다.
- 리소스 제공자가 권한 부여 서버에서 얻은 공개 키를 사용하여 JWT 토큰을 해독하고 검증합니다.
- 리소스 제공자가 토큰의 유효성을 기반으로 접근을 허용합니다.
OIDC에서의 사용 사례
OIDC (OpenID Connect)에서 불투명 토큰과 JWT는 다른 목적을 가지고 있으며, 다른 시나리오에서 사용됩니다.
불투명 토큰
- 사용자 프로필 검색:
기본적으로, 클라이언트가 별도의 리소스를 지정하지 않고 openid
스코프를 포함하여 액세스 토큰을 요청할 때, 권한 부여 서버는 불투명 액세스 토큰을 발행합니다. 이 토큰은 주로 OIDC /oidc/userinfo
엔드포인트에서 사용자 프로필 정보를 가져오는 데 사용됩니다. 불투명 액세스 토큰이 포함된 요청을 받으면, 권한 부여 서버는 내부 저장소를 확인하여 관련 사용 권한 정보를 검색하고 토큰의 유효성을 검증한 후 사용자 프로필 세부 정보를 응답합니다.
- 갱신 토큰 교환:
갱신 토큰은 클라이언트와 권한 부여 서버 간에만 교환되도록 설계되었으며, 리소스 제공자와 공유할 필요가 없습니다. 따라서 갱신 토큰은 일반적으로 불투명 토큰으로 발행됩니다. 현재의 액세스 토큰이 만료되면 클라이언트는 불투명 갱신 토큰을 사용하여 새 액세스 토큰을 얻을 수 있으며, 사용자를 재인증하지 않고도 지속적인 접근을 보장합니다.
JWT
- ID 토큰:
OIDC에서, ID 토큰은 사용자 정보를 포함하는 JWT이며 사용자를 인증하는 데 사용됩니다. 일반적으로 액세스 토큰과 함께 발행되며, ID 토큰은 클라이언트가 사용자의 신원을 확인할 수 있도록 합니다. 예를 들어:
// ID 토큰의 디코딩된 페이로드
{
"iss": "<https://logto.io>",
"sub": "1234567890",
"aud": "client_id",
"exp": 1630368000,
"name": "John Doe",
"email": "[email protected]",
"picture": "<https://example.com/johndoe.jpg>"
}
클라이언트는 ID 토큰을 검증하여 사용자의 신원을 확인하고 개인화 또는 권한 부여 목적으로 사용자 정보를 추출할 수 있습니다. ID 토큰은 한 번만 사용하는 것이며 API 리소스 권한 부여에 사용되어서는 안 됩니다.
- API 리소스 접근 (액세스 토큰 사용):
클라이언트가 특정 리소스 지시자로 액세스 토큰을 요청할 때, 권한 부여 서버는 해당 리소스를 접근하기 위한 JWT 액세스 토큰을 발행합니다. JWT는 리소스 제공자가 클라이언트의 접근 권한을 부여하는 데 사용할 수 있는 클레임을 포함합니다. 예를 들어:
// JWT 액세스 토큰의 디코딩된 페이로드
{
"iss": "<https://dev.logto.app>",
"sub": "1234567890",
"aud": "<https://api.example.com>",
"scope": "read write",
"exp": 1630368000
}
리소스 제공자는 클레임을 확인하여 요청을 검증할 수 있습니다:
iss
: 토큰이 신뢰할 수 있는 권한 부여 서버에 의해 발급된 것임을 확인합니다.sub
: 토큰과 연관된 사용자를 식별합니다.aud
: 특정 리소스를 위한 토큰인지 확인합니다.scope
: 사용자에게 부여된 권한을 검증합니다.