복합 ID(Composite Identity)
GitLab v19.1보안상의 이유로, 쓰기 작업을 수행하는 GitLab 플랫폼의 AI 생성 활동에는 복합 ID를 사용해야 합니다. 복합 ID 토큰을 생성하려면 다음이 필요합니다: 기본 토큰 소유자인 서비스 계정 사용자. 인스턴스 전체 또는 그룹 범위로 지정할 수 있습니다.
보안상의 이유로, 쓰기 작업을 수행하는 GitLab 플랫폼의 AI 생성 활동에는 복합 ID를 사용해야 합니다.
복합 ID를 사용하는 기능:
사전 요구 사항#
복합 ID 토큰을 생성하려면 다음이 필요합니다:
기본 토큰 소유자인 서비스 계정 사용자.
인스턴스 전체 또는 그룹 범위로 지정할 수 있습니다. 인스턴스 전체 계정은 GitLab 자체 기능에 일반적으로 사용되며, 고객 에이전틱 플로는 범위를 좁게 지정한 계정을 사용해야 합니다.
-
특정 권한이 있는 사용자만 서비스 계정을 생성할 수 있습니다. 계정이 생성되는 위치와 방법을 미리 계획하세요.
-
서비스 계정은 여러 기능에서 공유할 수 있지만, 동일한 계정을 공유하면 UI에서 작업을 구분하기 어려워집니다(예: 동일 사용자가 MR을 생성하고 리뷰하는 것처럼 보일 수 있음).
가용성 및 라이선스:
서비스 계정은 Premium 및 Ultimate에서 사용 가능합니다.
-
따라서, 복합 ID는 GitLab Premium 또는 Ultimate 라이선스가 필요합니다.
서비스 계정에는 composite_identity_enforced가 true로 설정되어 있어야 합니다.
이 설정은 서비스 계정 생성 UI에서는 제공되지 않으며, 프로그래밍 방식으로 구성해야 합니다.
복합 ID에 사용되는 OAuth 애플리케이션은 user:*의 동적 스코프를 활성화해야 합니다.
이 스코프는 OAuth 애플리케이션 UI에서 제공되지 않으며, 프로그래밍 방식으로 구성해야 합니다.
복합 ID 토큰 생성 방법#
OAuth 토큰만 지원됩니다.
-
서비스 계정은 로그인할 수 없는 봇 사용자이므로, 일반적인 인가 코드 플로(브라우저 동의)는 작동하지 않습니다.
-
서드파티 서비스와 통합하는 경우:
서비스 계정 + OAuth 앱에 대한 OAuth 그랜트를 수동으로 생성하세요. Amazon Q 예시를 참고하세요.
-
AI 요청을 시작한 사람 사용자에 대한 구체적인 동적 스코프가 그랜트 스코프에 포함되어야 하며,
user:$ID형식으로 지정합니다(예:user:123). 필요에 따라 다른 스코프도 포함하세요(예:api). -
https://gitlab.example.com/oauth/token을 통해 그랜트를 액세스 토큰으로 교환합니다. -
서드파티 서비스와 통합하지 않는 경우:
액세스 그랜트 단계를 건너뛰고 OAuth 액세스 토큰을 직접 생성할 수 있으며, 스코프에 user:$ID와 필요한 기본 스코프를 포함시킵니다.
-
grant_type=refresh_token을 사용하여 표준https://gitlab.example.com/oauth/token>엔드포인트를 통해 토큰을 갱신합니다. -
반환된 액세스 토큰은 서비스 계정에 속하지만, 스코프에
user:$ID를 포함합니다. 표준 OAuth 액세스 토큰처럼 갱신됩니다.
최소 예시#
- OAuth 애플리케이션 생성(이 단계는 맞춤형 OAuth 앱을 원하는 경우에만 필요합니다. GitLab Duo Workflow 기본 OAuth 애플리케이션과 서비스 계정은
ee/app/services/ai/duo_workflows/onboarding_service.rb를 호출하여 생성됩니다):
# Rails console
app = Authn::OauthApplication.new(
name: "Composite Identity App",
redirect_uri: Gitlab::Routing.url_helpers.root_url, # unused but cannot be nil
scopes: ::Gitlab::Auth::AI_WORKFLOW_SCOPES + [::Gitlab::Auth::DYNAMIC_USER],
trusted: false,
confidential: false # public client (no secret required)
)
app.save!
- 서비스 계정 + 사람 사용자에 대한 인가 그랜트 생성:
# Assuming you want to create a composite OAuth token for the GitLab Duo Workflow OAuth application and service account + root user in your GDK.
org = Organizations::Organization.default_organization
user = User.first
oauth_token_service = Ai::DuoWorkflows::CreateCompositeOauthAccessTokenService.new(
current_user: user,
organization: org,
).execute
oauth_token_service.payload[:oauth_access_token].plaintext_token
인가 평가 방법#
복합 ID 토큰으로 만든 요청은 다음 두 조건이 모두 참인 경우에만 인가됩니다:
-
서비스 계정이 해당 리소스에 접근할 수 있어야 합니다.
-
토큰 스코프의
user:$ID로 식별된 사람 사용자가 해당 리소스에 접근할 수 있어야 합니다.
요청 컨텍스트와 current_user#
요청에 복합 ID OAuth 토큰이 포함된 경우, Rails 요청 컨텍스트는 current_user를 user:$ID 스코프에서 추출한 사람 사용자로 재정의합니다. 토큰 자체는 서비스 계정에 속하지만, 요청을 시작한 사용자가 현재 사용자로 간주됩니다. 즉:
-
current_user에 의존하는 모든 코드는 사람 사용자로 실행됩니다. -
resolve_composite_identity_actor를 사용하여 귀속에 대한 올바른 액터를 결정하세요. 결과는 현재 요청에 대해 ID가 연결된 방식에 따라 달라집니다.
올바른 액터에 작업 귀속하기#
모든 쓰기 작업에 대한 액터를 확인할 때는 항상 resolve_composite_identity_actor를 사용하세요. 컨텍스트를 직접 판단할 필요가 없습니다. 컨텍스트는 요청 경계에서 자동으로 설정됩니다:
actor = Gitlab::Auth::Identity.resolve_composite_identity_actor(current_user)
작성자를 설정하는 곳(예: 노트, 이슈/MR, 커밋, 파이프라인 사용자 컨텍스트)에서 반환된 actor를 사용하세요.
메서드는 상황에 맞는 올바른 액터를 반환합니다:
-
서비스 계정이 요청한 경우(OAuth 토큰에
user:$ID스코프 포함, 또는 CI job): 내부적으로:authentication컨텍스트로 태깅됩니다. 서비스 계정을 반환합니다. SA가 액터이며 귀속되어야 합니다. -
사람이 요청했으나 SA가 부수적으로 연결된 경우(예: 사람이 SA를 리뷰어로 지정): 내부적으로
:permission_check컨텍스트로 태깅됩니다. 사람을 반환합니다. 사람이 액터이며 귀속되어야 합니다. -
복합 ID가 없는 경우:
current_user를 그대로 반환합니다.
시나리오에 따라 이 메서드를 다르게 호출할 필요가 없습니다. ID 시스템이 요청 인증 시 컨텍스트를 기록하며, resolve_composite_identity_actor가 자동으로 사용합니다.
빠른 설정 확인#
# Expect 200 only if BOTH the human and service account can read the project
curl --silent --show-error --fail --header "Authorization: Bearer " \
"https://gitlab.example.com/api/v4/projects/%2F"
일반적인 결과:
-
403: 주체 중 하나가 권한이 없는 경우.
-
404: 어느 주체도 리소스를 볼 수 없거나, 리소스를 찾을 수 없는 경우.
로컬/GDK 테스트 팁#
# Rails console
service_account = User.find_by_username("service_account")
service_account.update!(composite_identity_enforced: true)
문제 해결#
-
동적 스코프가 거부되거나 무시되는 경우: OAuth 앱에
dynamic_scopes: "user:*"가 설정되어 있는지 확인하세요. -
토큰에
user:$ID가 없는 경우: 스코프에 구체적인user:$ID를 포함하여 그랜트/토큰을 재발급하세요. -
플랫폼 작업이 사람 대신 서비스 계정에(또는 반대로) 귀속되는 경우: ID가 연결된 컨텍스트를 확인하세요. OAuth/CI 플로는
:authentication을 사용하며(SA에 귀속), 웹/할당 플로는:permission_check를 사용합니다(사람에 귀속). 올바른 액터에 작업 귀속하기를 참고하세요. -
토큰 교환 시 422 오류: 리다이렉트 URI 불일치 또는 그랜트 만료.
-
API 요청 시 403 오류: 두 주체 모두 필요한 프로젝트/그룹 권한이 있는지, 기본 스코프(예:
api)가 포함되어 있는지 확인하세요.