개인 GitHub 저장소와 다른 Organization 저장소에서는 문제없이 Push할 수 있었는데, 특정 Organization 저장소에서만 Android Studio가 계속 GitHub 로그인을 요구하는 문제가 발생했다.
처음에는 GitHub PAT, SSH 키, macOS Keychain 문제를 의심했다. 하지만 최종 원인은 전혀 다른 곳에 있었다.
바로 GitHub Organization에서 JetBrains IDE Integration OAuth 앱의 접근 권한이 승인되지 않은 것이었다.
문제 상황
현재 프로젝트의 원격 저장소는 HTTPS 방식으로 연결되어 있었다.
git remote -v
origin https://github.com/Nexters-Logit/logit-android.git (fetch)
origin https://github.com/Nexters-Logit/logit-android.git (push)
Android Studio에서 Push할 때마다 GitHub 로그인을 요구했지만, 이상하게도 다른 프로젝트에서는 같은 문제가 발생하지 않았다.
저장소에 대한 계정 권한도 Write였기 때문에 단순한 권한 부족으로 보이지 않았다.
Write 권한만 있으면 Push할 수 있는 것 아닌가?
GitHub의 Write 권한은 해당 계정이 저장소에 Push할 수 있다는 의미다.
하지만 권한과 인증은 별개다.
Write 권한
→ 이 계정이 Push해도 되는가?
PAT, OAuth 토큰 또는 SSH 키
→ Push를 시도한 사용자가 실제로 그 계정인가?
HTTPS 방식으로 GitHub에 Push하려면 GitHub 계정 비밀번호 대신 다음과 같은 인증 수단이 필요하다.
- Personal Access Token
- IDE나 GitHub CLI가 발급받은 OAuth 토큰
- Git Credential Helper에 저장된 인증 정보
직접 PAT를 발급받은 적이 없더라도 Android Studio에서 GitHub 로그인을 했다면 JetBrains OAuth 앱이 발급받은 토큰을 사용하고 있을 수 있다.
처음 의심했던 원인
처음에는 다음과 같은 원인을 확인했다.
- 원격 저장소 URL이 잘못됐는지
- 프로젝트에 별도의 Git credential 설정이 있는지
- Git LFS가 추가 인증을 요구하는지
- Push hook이 실행되는지
- macOS Keychain에 인증 정보가 저장됐는지
- GitHub PAT가 필요한지
- SSH 방식으로 변경해야 하는지
하지만 프로젝트의 Git 설정은 다른 프로젝트와 특별히 다르지 않았다.
터미널에서 PAT를 macOS Keychain에 저장한 뒤에는 Push가 정상적으로 동작했다.
git push --dry-run
To https://github.com/example/example.git
<old-commit>..<new-commit> feature/example -> feature/example
반면 Android Studio에서는 여전히 로그인을 요구했다.
이 결과를 통해 저장소 권한이나 PAT 자체가 아니라 Android Studio의 인증 경로에 문제가 있다는 것을 알 수 있었다.
Android Studio의 인증 방식
Android Studio에는 GitHub와 관련된 인증 경로가 두 가지 존재할 수 있다.
JetBrains IDE Integration OAuth
→ Android Studio가 관리하는 GitHub 로그인
Git Credential Helper
→ macOS Keychain 등에 저장된 Git 인증 정보
Android Studio의 다음 설정에서 Use credential helper를 활성화하면 터미널 Git과 동일한 인증 정보를 사용할 수 있다.
Settings
→ Version Control
→ Git
→ Use credential helper
이 설정을 활성화하자 Android Studio에서도 Push가 성공했다.
하지만 여기서 한 가지 의문이 남았다.
왜 다른 프로젝트에서는 credential helper 설정 없이도 Push할 수 있었을까?
특정 Organization에서만 실패한 진짜 원인
GitHub 개인 설정에서 Android Studio가 사용하는 OAuth 앱을 확인했다.
GitHub Settings
→ Applications
→ Authorized OAuth Apps
→ JetBrains IDE Integration
확인 결과 해당 Organization에 대한 접근 권한이 부여되어 있지 않았다.
즉, 상황은 다음과 같았다.
다른 저장소
→ JetBrains OAuth 앱이 Resource owner 접근 가능
→ 기존 OAuth 토큰으로 Push 성공
특정 Organization의 저장소
→ JetBrains OAuth 앱의 Organization 접근 미승인
→ Android Studio에서 인증 실패
→ GitHub 로그인 반복
Organization Owner 계정으로 해당 OAuth 앱의 접근을 Grant한 뒤 문제가 해결됐다.
Organization 설정에서도 관련 내용을 확인할 수 있다.
Organization
→ Settings
→ Third-party access
GitHub Organization은 보안을 위해 승인되지 않은 OAuth 앱이 조직의 비공개 저장소에 접근하지 못하도록 제한할 수 있다. 따라서 같은 GitHub 계정과 IDE를 사용하더라도 Organization마다 동작이 달라질 수 있다.
PAT와 SSH 키는 필요했을까?
이번 문제를 해결하는 과정에서 fine-grained PAT를 발급하고 SSH 키도 등록했지만, 최종 원인만 놓고 보면 필수적인 작업은 아니었다.
기존처럼 Android Studio의 GitHub OAuth 로그인을 사용하려면 Organization에서 JetBrains IDE Integration 접근을 승인하면 된다.
PAT를 사용하는 경우에는 다음 조건이 필요하다.
- Resource owner가 대상 Organization으로 설정되어 있어야 함
- 대상 저장소가 접근 범위에 포함되어 있어야 함
- Contents: Read and write 권한이 있어야 함
- Organization의 승인이 필요한 경우 PAT 상태가 Active여야 함
SSH를 사용하는 경우에는 원격 저장소 URL도 SSH 형식으로 변경해야 한다.
git remote set-url origin git@github.com:Nexters-Logit/logit-android.git
SSH 키만 등록하고 remote URL을 HTTPS로 유지하면 Push에는 계속 HTTPS 인증이 사용된다.
정리
특정 GitHub Organization 저장소에서만 Android Studio가 로그인을 반복해서 요구한다면 다음 순서로 확인하는 것이 좋다.
- 터미널에서 git push --dry-run을 실행한다.
- 터미널에서는 성공하고 IDE에서만 실패하는지 확인한다.
- Android Studio의 Use credential helper 설정을 확인한다.
- GitHub의 Authorized OAuth Apps에서 JetBrains 앱을 확인한다.
- 대상 Organization에 대한 앱 접근이 Granted 상태인지 확인한다.
- Organization의 Third-party access 정책을 확인한다.
이번 문제의 핵심은 저장소의 Write 권한이나 Git 설정이 아니었다.
Android Studio가 사용하는 JetBrains OAuth 앱이 해당 GitHub Organization에 접근하도록 승인되지 않은 것이 원인이었다.
인증 문제를 해결할 때는 PAT를 새로 만들거나 SSH로 변경하기 전에, “현재 IDE가 어떤 인증 수단을 사용하고 있으며 그 인증 수단이 해당 Organization에서 허용됐는가?”를 먼저 확인하는 것이 좋다.
'기타 etc.' 카테고리의 다른 글
| Softeer 데브크루 사용 후기 (0) | 2024.02.07 |
|---|---|
| 듀가드 맥북에서 갑자기 안 될 때 (0) | 2024.01.23 |
| EA app 로그인 안되는 현상 (0) | 2023.02.12 |
| Github Secrets 사용 시 업스트림 Actions CI 에러 (0) | 2021.11.17 |
| ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) (0) | 2021.08.06 |