GitHub SSO 인증 자동화 — classic PAT·SSH 키를 앱으로 최대 50개 조직에 한 번에 승인
GitHub Enterprise Cloud에서 classic 개인 액세스 토큰(PAT)과 SSH 키의 SSO 인증을 GitHub 앱으로 자동화할 수 있게 됐다(9월 16일 공지). 엔터프라이즈에 설치한 앱이 API 요청 한 번으로 자격증명 하나를 최대 50개 조직에 한꺼번에 승인한다. 공지는 이를 "credential delegation"(자격증명 위임)이라고 부른다.
SSO 인증이 뭐길래 자동화가 필요했나
SSO(통합 로그인)를 강제하는 조직에서는 classic PAT나 SSH 키를 만들었다고 바로 쓸 수 없다. 조직마다 한 번씩 승인(authorize)을 받아야 한다. GitHub 문서의 설명이다. "To use a personal access token (classic) with an organization that uses single sign-on (SSO), you must first authorize the token."(SSO를 쓰는 조직에서 classic PAT를 쓰려면 먼저 토큰을 승인해야 한다.)
지금까지는 이걸 사람이 손으로 했다. 토큰 설정 화면의 Configure SSO에서 조직 옆 Authorize를 하나씩 누르는 식이다. 승인을 안 하면 API는 404나 403을 돌려준다. 403일 때는 응답 헤더 X-GitHub-SSO에 승인 링크가 담겨 오는데, 이 링크는 1시간 뒤 만료된다. gh 같은 도구에선 "Resource protected by organization SAML enforcement." 같은 오류로 보인다.
공지가 짚은 문제는 그다음이다. "manually authorizing credentials one organization at a time creates friction and overhead, leading to the use of long-lived tokens to avoid rotation."(조직마다 수동으로 승인하는 번거로움이 결국 교체를 피하려고 수명이 긴 토큰을 쓰게 만든다.) 새 토큰으로 바꿀 때마다 수십 개 조직에서 다시 승인해야 하니, 아예 안 바꾸게 된다는 얘기다. 이번 기능은 그 비용을 없애서 토큰 교체를 쉽게 하려는 것이다.
fine-grained PAT는 대상이 아니다. 문서에 이유를 짐작할 문장이 있다. "Fine-grained personal access tokens are authorized during token creation, before access to the organization is granted."(fine-grained PAT는 만들 때 이미 조직 승인을 거친다.) 나중에 조직별로 따로 승인하는 구조가 아니니 위임할 일도 없다.
켜기 전에 필요한 것 세 가지
- 엔터프라이즈 수준 SSO — 조직마다 따로 SAML을 켠 구성은 대상이 아니다. 엔터프라이즈 전체에 SSO를 강제한 경우만 된다.
- 설정 켜기 — "By default, enterprise-installed GitHub Apps cannot authorize credentials."(기본적으로 엔터프라이즈 설치 앱은 자격증명을 승인할 수 없다.) Settings → Authentication security → Credentials의 "Allow GitHub Apps to authorize credentials"를 켜야 한다.
- 엔터프라이즈에 설치한 앱과 권한 — 엔터프라이즈나 그 안의 조직이 소유한 앱이어야 하고, "Enterprise credentials" 권한(쓰기, 공지 표기
enterprise_credentials:write)이 필요하다. 엔터프라이즈 설치 앱 자체는 아직 공개 프리뷰다.
GitHub은 요청을 받으면 세 가지를 확인한다. 대상 조직이 이 엔터프라이즈 소속인지, 자격증명 주인이 각 조직의 멤버인지, 엔터프라이즈 수준 SSO를 쓰는지다. 이미 승인된 조직은 건너뛴다. 비밀값도 오가지 않는다. 공지 표현으로 "Identifies the credential by its non-secret token ID or SSH key fingerprint, so no credential secrets are passed to the GitHub App."(비밀이 아닌 토큰 ID나 SSH 키 지문으로 식별하므로 앱에 비밀값이 넘어가지 않는다.)
API 요청, 이렇게 보낸다
GitHub 문서의 예시다.
curl --request POST \
--url "https://api.github.com/enterprises/ENTERPRISE/credential-authorizations" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer INSTALLATION-ACCESS-TOKEN" \
--header "X-GitHub-Api-Version: 2026-03-10" \
--data '{
"credential_id": 12345678,
"credential_type": "classic_pat",
"organizations": ["ORGANIZATION-1", "ORGANIZATION-2"]
}'
토큰 자리엔 엔터프라이즈 설치 액세스 토큰만 쓸 수 있다. 문서는 "Organization installation access tokens, user access tokens, and personal access tokens are not supported."(조직 설치 토큰, 사용자 토큰, PAT는 지원하지 않는다)라고 못 박았다. 이 토큰은 앱 개인 키로 발급하는 1시간짜리 토큰이다. "cannot be scoped down"(범위를 좁힐 수 없다)이라는 점도 기억해 둘 만하다.
credential_type에 따라 credential_id의 모양이 다르다.
| 유형 | credential_type | credential_id |
|---|---|---|
| classic PAT | classic_pat | 정수(토큰 ID) |
| SSH 키 | ssh_key | 문자열(SHA256:... 지문) |
성공하면 201이 오고, 다른 요청이 같은 자격증명을 수정 중이면 409가 온다. 이 요청 형식이 실제 공개 API 명세에도 그대로인지 확인해 봤다. GitHub의 공개 명세 저장소(github/rest-api-description)에서 Enterprise Cloud용 명세 파일(9월 29일 커밋)을 받아 열어 보니, organizations는 최소 1개·최대 50개, 중복 불가로 정의돼 있었다. 같은 경로엔 승인 외에 일괄 취소, 전체 폐기 같은 작업 6개가 함께 묶여 있었다.
식별자 찾기와, 앱이 뚫렸을 때
실무에서 가장 헷갈리는 건 ID다. 조직 API(GET /orgs/{org}/credential-authorizations) 응답에는 비슷한 이름이 둘 있다. PAT 자체의 ID는 authorized_credential_id이고, credential_id는 "이 조직에서의 승인 기록" ID다. 이름만 보고 credential_id를 가져다 쓰면 엉뚱한 값을 넣게 된다. classic PAT의 ID는 토큰 설정 URL(/settings/tokens/숫자) 끝 숫자나 감사 로그의 token_id에서도 찾을 수 있다. SSH 키 지문은 ssh-keygen -lf 키파일.pub로 확인한다.
권한 구조도 한 번 따져 봐야 한다. 같은 "Enterprise credentials" 권한 하나로 승인 발급, 위임 승인 일괄 취소, 엔터프라이즈 전체 자격증명 폐기, 자격증명 인벤토리 조회까지 할 수 있다. 이 앱의 개인 키가 새면 영향이 크다는 뜻이다.
게다가 앱을 지워도 승인은 남는다. 문서 문장이다. "Credential authorizations remain active if the GitHub App is uninstalled or loses permission."(앱을 제거하거나 권한을 잃어도 승인은 유지된다.) 앱이 의심되면 DELETE /enterprises/{enterprise}/credential-authorizations에 integration_id를 지정해, 그 앱이 만든 승인만 따로 회수해야 한다. SAML 로그인 등 다른 경로로 받은 승인은 영향을 받지 않는다. 이때 거둬지는 건 앱이 내준 승인이지 키나 토큰 자체가 아니다 — 자격증명까지 없애는 폐기는 별도 API 옵션(revoke_credentials)으로 나뉘어 있다.
이 흐름은 올해 GitHub이 쌓아 온 자격증명 도구의 연장선이다. 2월 사고 대응용 일괄 취소, 3월 폐기 API 확장, 6월 사용자별 긴급 폐기, 8월 유형별 취소까지는 전부 '거둬들이는' 쪽이었다. 9월 16일 이번 기능이 처음으로 '내주는' 쪽이고, 닷새 뒤 인벤토리 내보내기가 '들여다보는' 쪽을 채웠다.
개인적으로는 서비스 계정 토큰을 90일마다 교체하는 팀이 가장 반길 기능이라고 본다. 교체할 때마다 조직 수십 곳을 돌던 일을 API 한 번으로 줄일 수 있다. 대신 그 API를 부르는 앱은 엔터프라이즈에서 가장 민감한 자산이 된다. 앱 개인 키를 어디에 두고 누가 꺼낼 수 있는지부터 정해 두고 켜는 게 순서다.
인용한 GitHub 문서(docs.github.com)는 CC BY 4.0, REST API 명세는 MIT 라이선스이며, GitHub 체인지로그는 원문을 짧게 인용했다.