프로젝트에 OIDC 서버 통합하기 위한 완벽 가이드
프로젝트에 OIDC (OpenID Connect) 서버를 통합하는 모범 사례를 배우고, 무대에서 구성 요소 간의 상호 작용을 이해하세요.
중앙 집중식 인증 및 권한 부여 시스템이 필요한 상황에 직면할 수 있습니다. 흔히 이것은 아이덴티티 액세스 관리 (IAM) 또는 아이덴티티 제공자 (IdP)로 알려져 있습니다. 때때로 사람들은 Customer IAM 또는 Workforce IAM처럼 사업에 따라 이러한 용어를 첨가합니다.
이러한 멋진 이름들을 잠시 접어둡시다. IAM이 필요한 이유는 애플리케이션이 성장하고 있거나 처음부터 복잡한 작업을 벤더에게 위임할 계획을 가지고 있기 때문일 수 있습니다. 어쨌든, 당신의 프로젝트에 새로운 아이덴티티 시스템이 도입되어야 할 시점에 이르게 됩니다.
OAuth 2.0의 인기를 고려할 때, OpenID Connect (OIDC)는 많은 개발자들에게 자연스러운 선택입니다. OIDC는 OAuth 2.0 위에 구축된 인증 계층이기 때문에, OIDC를 처음 사용할 때 친숙함을 느낄 수 있습니다. 이제 시작합시다!
OIDC 서버란 무엇이며, OIDC 서버를 통합해야 하는 이유는 무엇인가요?
OIDC 서버, 혹은 아이덴티티 제공자는 사용자 인증 및 권한 부여를 관리하는 중앙 집중식 시스템입니다. 다중 애플리케이션 사업을 위한 중앙 집중식 아이덴티티 시스템이 필요한 이유에서 논의했듯이, 중앙 집중식 아이덴티티 시스템은 많은 이점을 제공합니다.
예를 들어 프로젝트가 간단한 웹 애플리케이션으로 시작하여 인증 기능을 내장했다면:
프로젝트가 성장함에 따라 모바일 버전을 도입해야 합니다:
각 애플리케이션에 대해 계정을 만들어야 한다면 이는 사용자에게 나쁜 경험이 될 것입니다. 웹 애플리케이션을 시작했기 때문에 모바일 애플리케이션을 통해 웹 애플리케이션과 통신하여 인증을 처리하도록 허용했습니다:
이제 새로운 API 서비스가 도입되고 있습니다. 유료 사용자를 위한 서비스이기 때문에 사용자가 서비스에 액세스할 수 있도록 인증 및 권한 부여가 필요합니다. 이를 달성하려면 웹 애플리케이션을 통해 서비스를 프록시할 수 있습니다:
사용자 인증을 위해 일부 토큰 기술을 사용하고, 서비스에서 웹 애플리케이션과의 대화를 통해 토큰을 검증할 수 있습니다. 따라서 모바일 애플리케이션은 서비스에 직접 액세스할 수 있습니다:
상황이 복잡해지고 있습니다. 따라서 인증 및 권한 부여 논리를 별도의 서비스로 분리하기로 결정합니다:
리팩토링 과정은 고통스러울 수 있습니다. 프로젝트에 더 많은 애플리케이션과 서비스를 추가할수록 복잡성이 기하급수적으로 증가할 수 있음을 알 수 있습니다. 더욱이, 비밀번호 없는 로그인, 소셜 로그인, SAML 등 여러 인증 방법을 유지 관리해야 할 수도 있습니다.
프로젝트를 확장할 계획이 있을 때, 초기 단계에서 아이덴티티 제공자를 도입하는 것이 좋습니다.
OIDC 서버 통합을 위한 모범 사례
OIDC 제공자 찾기
시장에는 많은 OIDC 제공자가 있습니다. 요구 사항과 선호도에 따라 하나를 선택할 수 있습니다. 제공자가 OIDC 호환성만 갖추고 있다면 프로젝트에서 동일한 역할을 수행할 것입니다.
OIDC에서 “주제,” “클라이언트,” 및 “청중”은 무엇을 의미하나요?
개념을 간소화하기 위해, 주제는 클라이언트를 통해 청중에 접근을 요청하는 엔티티로 생각할 수 있습니다.
일부 일반적인 시나리오를 보겠습니다:
1. 사용자가 웹 애플리케이션에서 로그인 버튼을 클릭
전통적이며 서버 측 렌더링을 사용하는 웹 애플리케이션에서는 프론트엔드와 백엔드가 결합되어 있습니다. 웹 애플리케이션이 프론트엔드와 백엔드를 모두 제공한다고 가정해 보겠습니다:
- 주제: 사용자
- 청중: OIDC 서버
- 클라이언트: 웹 애플리케이션
청중이 OIDC 서버라는 것이 역설적일 수 있지만, 이는 최종 사용자에게 SSO (단일 로그인) 경험을 실현하는 핵심입니다. 인가 코드 흐름에 대한 간단한 시퀀스 다이어그램을 보겠습니다:
코드
는 액세스 토큰, ID 토큰, 리프레시 토큰과 같은 다양한 토큰으로 교환할 수 있는 일회성 코드입니다. 이 순간에는 이러한 토큰을 모두 이해할 필요는 없습니다. 앞으로 나아가면서 점점 더 잘 이해하게 될 것입니다.
위의 경우, 사용자가 OIDC 서버(청중)와 이미 인증했기 때문에 다른 애플리케이션으로 전환할 때 다시 로그인할 필요가 없습니다.
2. 사용자가 싱글 페이지 애플리케이션을 사용
싱글 페이지 애플리케이션(또는 모바일 애플리케이션)에서는 프론트엔드와 백엔드가 분리되어 있습니다. 백엔드는 API 서비스라고 가정해 보겠습니다:
- 주제: 사용자
- 청중: API 서비스
- 클라이언트: 싱글 페이지 애플리케이션 (SPA)
인가 코드 흐름을 가진 간단한 시퀀스 다이어그램:
API 서비스는 비대화형이기 때문에, SPA는 API 서비스에서 청중으로서 액세스 토큰(aud
토큰 사용)을 사용해야 합니다.
왜 OIDC 서버가 여전히 청중인가요?
기술적으로 OIDC 서버를 청중 목록에서 제거할 수 있습니다. 대부분의 경우, 사용자 정보를 OIDC 서버에서 가져야 하기 때문에(이는 OIDC 서버가 청중이 되어야 함을 요구), 사용자 상호작용이 필요할 때마다 OIDC 서버를 청중 목록에 항상 포함시키는 것이 좋습니다.
잠깐만요, 인가 요청에 여러 청중을 가질 수 있다는 건가요?
맞습니다! OIDC는 OAuth 2.0 위에 구축되어 있기 때문에, 인가 요청에 RFC 8707: Resource Indicators for OAuth 2.0을 활용하여 여러 청중을 지정할 수 있습니다. 이는 보조금과 OIDC 서버의 지원이 필요합니다. Logto는 이 기능을 기본적으로 지원합니다.
3. 머신 간 통신
서비스 A가 서비스 B를 호출해야 한다고 가정해 보겠습니다:
- 주제: 서비스 A
- 청중: 서비스 B
- 클라이언트: 서비스 A
클라이언트 자격증명 부여를 통한 간단한 시퀀스 다이어그램:
서비스 B가 서비스 A를 호출해야 할 경우, 역할을 간단히 바꿀 수 있습니다.
요약
- 주제: 사용자, 서비스, 또는 청중에게 접근이 필요한 어떤 엔티티일 수 있습니다.
- 클라이언트: 웹 애플리케이션, 모바일 애플리케이션, 또는 주제를 대신하여 요청을 시작하거나 행동하는 엔티티일 수 있습니다.
- 청중: 서비스, API, 또는 주제에게 접근을 제공하는 어떤 엔티티일 수 있습니다.
액세스 토큰, ID 토큰, 리프레시 토큰이란?
OIDC를 사용할 때 접하게 될 세 가지 유형의 토큰이 있습니다:
- 액세스 토큰: 청중에 접근하는 데 사용됩니다. JWT (JSON 웹 토큰) 또는 불투명 토큰(보통은 랜덤 문자열)일 수 있습니다.
- ID 토큰: OIDC 특정 토큰으로, 사용자 정보를 포함합니다. 항상 JWT입니다. 클라이언트는 토큰을 디코딩하여 사용자 정보를 얻을 수 있습니다.
- 리프레시 토큰: 액세스 토큰 또는 ID 토큰이 만료되면 새로운 토큰 세트를 얻는 데 사용됩니다.
이러한 토큰의 자세한 설명은 OIDC 프로토콜에서 리프레시 토큰, 액세스 토큰 및 ID 토큰 이해하기를 참조할 수 있습니다.
위의 시나리오 1과 2에서, 인가 요청이라는 용어는 특정 부여를 통해 토큰 세트를 얻기 위한 요청을 의미합니다.
모든 것이 잘 진행되면 "코드
를 사용하여 토큰 교환" 단계에서 토큰 세트가 반환됩니다. 세트에 있는 사용 가능한 토큰 유무는 여러 요소, 특히 인가 요청의 scope
매개변수에 따라 달라집니다. 단순화를 위해 모든 토큰이 세트에 반환된다고 가정합니다. 액세스 토큰이 만료되면 클라이언트는 사용자 상호작용 없이 리프레시 토큰을 사용하여 새로운 토큰 세트를 얻을 수 있습니다.
시나리오 3의 경우는 더 간단합니다. 클라이언트 자격증명 부여는 오직 액세스 토큰만 반환하기 때문입니다.
OIDC에서 여러 청중 처리하는 방법
한 번에 하나의 액세스 토큰만 반환된다는 것을 알아차릴 수 있습니다. 클라이언트가 여러 청중에 액세스해야 하는 경우를 어떻게 처리할 수 있을까요?
두 가지 일반적인 솔루션이 있습니다:
코드 교환 요청에서 resource
지정하기
클라이언트가 코드를 토큰으로 교환할 때 요청에 resource
매개변수를 지정할 수 있습니다. OIDC 서버는 지정된 청중에 대한 액세스 토큰을 해당하는 경우 반환할 것입니다.
여기에는 비표준적인 예시가 있습니다:
그런 다음 OIDC 서버는 해당하는 경우 API_SERVICE
청중에 대한 액세스 토큰을 반환할 것입니다.
리프레시 토큰을 사용하여 새로운 액세스 토큰 얻기
RFC 8707을 사용하여 클라이언트는 resource
매개변수를 여러 번 사용하여 여러 청중을 지정할 수 있습니다. 이제 클라이언트에 리프레시 토큰이 제공된다면 토큰을 새로 고칠 때 resource
매개변수에 청중을 지정할 수 있습니다.
여기에는 비표준적인 예시가 있습니다:
이는 이전 솔루션과 동일한 효과를 가집니다. 한편, 다른 승인된 청중은 이후의 토큰 요청에서 여전히 유효합니다.
클라이언트 자격증명 부여
클라이언트 자격증명 부여에서도 resource
매개변수를 사용하여 청중을 지정할 수 있습니다. 이 부여에서는 여러 청중에 대해 문제가 없습니다. 다른 청중에 대한 새 액세스 토큰을 요청하기 위해 간단히 다른 토큰 요청을 보내면 되기 때문입니다.
API 서비스 보호하기
시나리오 2의 "API 서비스"와 시나리오 3의 "서비스 B"는 공통점이 하나 있습니다: 요청이 인증되었는지 확인하기 위해 액세스 토큰의 유효성을 검증해야 합니다. 액세스 토큰의 형식에 따라 유효성 검증 과정은 다를 수 있습니다.
- 불투명 토큰: API 서비스는 토큰을 검증하기 위해 OIDC 서버에 요청해야 합니다. 이를 위해 OIDC 서버는 주로 인스트로스펙션 엔드포인트를 제공합니다.
- JWT: API 서비스는 토큰의 서명 및 클레임을 확인하여 로컬에서 토큰을 검증할 수 있습니다. OIDC 서버는 API 서비스에 서명을 검증할 공개 키를 가져올 수 있는 JSON 웹 키 세트 (JWKS) 엔드포인트 (
jwks_uri
)를 제공하는 경우가 많습니다.
JWT에 익숙하지 않은 경우 JSON 웹 토큰 (JWT)란 무엇인가? 기사를 참조할 수 있습니다. 사실, 보통 수동으로 서명과 주장을 확인할 필요는 없습니다. 왜냐하면 jose와 같은 Node.js 및 웹 브라우저용 라이브러리들이 이를 대신해 주기 때문입니다.
주장 확인하기
JWT 서명을 검증하는 것 외에도, API 서비스는 항상 토큰의 주장을 확인해야 합니다:
iss
: 토큰의 발행자. OIDC 서버의 발행자 URL과 일치해야 합니다.aud
: 토큰의 청중. API 서비스의 청중 값(주로 유효한 URI)과 일치해야 합니다.exp
: 토큰의 만료 시간. 만료되었을 경우 API 서비스는 토큰을 거절해야 합니다.scope
: 토큰의 스코프(권한). API 서비스는 필요한 스코프가 토큰에 있는지 확인해야 합니다.
sub
(주제) 및 iat
(발행 시각)과 같은 다른 주장도 경우에 따라 중요할 수 있습니다. 추가적인 보안 조치가 있는 경우, 주장들을 적절히 확인하세요.
권한 부여
여전히 답이 없는 질문이 있습니다: 어떻게 스코프 (즉, 권한)가 주제에게 부여될 수 있는지를 결정할까요?
하나의 질문이 새로운 권한 부여 세계를 열어주며, 이는 이 글의 범위를 벗어납니다. 간단히 말해서, RBAC (역할 기반 접근 제어)와 ABAC (속성 기반 접근 제어) 같은 일반적인 접근 방식들이 있습니다. 시작하는 데 도움이 되는 몇 가지 자료는 다음과 같습니다:
마무리 노트
프로젝트에 OIDC 서버를 도입하는 것은 큰 걸음입니다. 이는 프로젝트의 보안성과 확장성을 크게 향상시킬 수 있습니다. 한편, 개념과 구성 요소 간의 상호작용을 이해하는 데 시간이 걸릴 수 있습니다.
요구 사항과 선호도에 맞는 좋은 OIDC 제공자를 선택하면 통합 과정의 복잡성을 현저히 줄일 수 있습니다. 제공자는 일반적으로 OIDC 서버, 권한 메커니즘, SDK, 그리고 향후 필요할 수 있는 엔터프라이즈 기능을 포함한 전체 패키지를 제공하기 때문입니다.
이 가이드가 OIDC 서버 통합의 기본을 이해하는 데 도움이 되었기를 바랍니다. 시작할 서비스를 찾고 있다면, 저희의 개발자를 위한 아이덴티티 인프라인 Logto를 추천드립니다.