Google API 호출하기 - OAuth 2.0 Service Account 인증
게임 서버를 운영하다 보면 Google API를 호출해야 할 일이 많다. 영수증 검증, 환불 처리, 구독 관리 등등. 이 모든 API는 OAuth 2.0 인증이 필요한데, 처음 접하면 개념이 좀 복잡하다.
서버에서 Google API를 호출하기 위한 인증 체계를 정리한다. 특히 Service Account를 활용한 서버 to 서버 인증에 집중한다.
1. 게임 서버에서 자주 쓰는 Google API
영수증 조회 API
GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}
인앱 결제 영수증이 유효한지 확인한다.
환불 API
POST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/orders/{orderId}:refund
특정 주문을 환불 처리한다.
최근 환불 조회 API
GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/voidedpurchases
최근에 환불된 영수증 목록을 조회한다. 어뷰징 탐지에 유용하다.
2. OAuth 2.0 인증 플로우
Google API는 OAuth 2.0 프로토콜로 인증한다. 상황에 따라 여러 플로우가 있는데, 서버에서 사용하는 건 Client Credentials Flow다.
Authorization Code Flow (프론트엔드 + 백엔드)
소셜 로그인(구글 로그인)에서 주로 사용한다:
sequenceDiagram
participant User
participant Frontend
participant Backend
participant Google
User->>Frontend: 로그인 버튼 클릭
Frontend->>Google: 인가 요청
Google->>User: 로그인 및 동의 화면
User->>Google: 동의
Google-->>Frontend: Authorization Code
Frontend->>Backend: Code 전달
Backend->>Google: Code로 Token 요청
Google-->>Backend: Access TokenClient Credentials Flow (서버 to 서버)
Service Account를 사용하는 서버 간 통신:
sequenceDiagram
participant Server
participant GoogleAuth as Google OAuth
participant GoogleAPI as Google API
Server->>Server: JWT 생성 (Private Key 서명)
Server->>GoogleAuth: JWT로 Access Token 요청
GoogleAuth-->>Server: Access Token 발급
Server->>GoogleAPI: Access Token으로 API 호출
GoogleAPI-->>Server: 응답게임 서버에서 Google API를 호출할 때는 두 번째 플로우를 사용한다.
3. Google Cloud Service Account란?
Service Account는 사람이 아닌 프로그램이 인증을 수행하는 용도의 특별한 계정이다.
일반 Gmail 계정과 달리:
- 비밀번호 대신 Private Key를 사용
- 사용자 동의 과정 없이 자동으로 토큰 발급
- 특정 프로젝트/리소스에 대한 권한 부여 가능
Service Account 인증 파일
{
"type": "service_account",
"project_id": "my-game-project",
"private_key_id": "abc123...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "payment@my-game-project.iam.gserviceaccount.com",
"client_id": "123456789",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}이 파일을 서버에 안전하게 저장하고, API 호출 시 사용한다.
4. JWT Bearer Token Flow
Service Account로 Access Token을 발급받는 과정을 JWT Bearer Token Flow라고 한다.
단계별 설명
1. JWT 생성
Service Account 인증 파일의 정보로 JWT를 만든다:
{
"iss": "payment@my-game-project.iam.gserviceaccount.com",
"aud": "https://oauth2.googleapis.com/token",
"scope": "https://www.googleapis.com/auth/androidpublisher",
"iat": 1704067200,
"exp": 1704070800
}iss: client_email (발급자)aud: token_uri (토큰 요청 URL)scope: 호출할 API의 권한 범위
JWT는 RS256 알고리즘으로 private_key를 사용해 서명한다.
2. Access Token 요청
생성한 JWT를 Google OAuth 서버에 제출:
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={JWT}3. Access Token 발급
Google은 JWT를 검증하고 Access Token을 반환한다:
{
"access_token": "ya29.a0...",
"token_type": "Bearer",
"expires_in": 3600
}4. API 호출
발급받은 토큰으로 API를 호출한다:
GET https://androidpublisher.googleapis.com/...
Authorization: Bearer ya29.a0...5. 코드로 구현하기 (C#)
Google.Apis 패키지 사용
using Google.Apis.Auth.OAuth2;
using Google.Apis.AndroidPublisher.v3;
using Google.Apis.Services;
public class GoogleApiService
{
private readonly AndroidPublisherService _publisherService;
public GoogleApiService(string serviceAccountKeyPath)
{
// Service Account 인증 파일 로드
var credential = GoogleCredential
.FromFile(serviceAccountKeyPath)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
// Android Publisher 서비스 초기화
_publisherService = new AndroidPublisherService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "MyGameServer"
});
}
public async Task<ProductPurchase> GetPurchaseAsync(
string packageName,
string productId,
string purchaseToken)
{
var request = _publisherService.Purchases.Products.Get(
packageName,
productId,
purchaseToken);
return await request.ExecuteAsync();
}
}사용 예시
var googleApi = new GoogleApiService("/secrets/service-account.json");
var purchase = await googleApi.GetPurchaseAsync(
"com.mygame.app",
"gem_100",
"token_abc123...");
if (purchase.PurchaseState == 0) // Purchased
{
// 상품 지급 처리
}6. Shell에서 테스트하기
개발/디버깅 시 Shell에서 직접 API를 호출할 수 있다.
gcloud CLI 설치 및 인증
# Service Account 인증
gcloud auth activate-service-account --key-file=./service-account.json
# 현재 인증된 계정 확인
gcloud auth listAccess Token 발급
# scope를 지정하여 토큰 발급
ACCESS_TOKEN=$(gcloud auth print-access-token \
--scopes=https://www.googleapis.com/auth/androidpublisher)토큰 정보 확인
curl "https://oauth2.googleapis.com/tokeninfo?access_token=$ACCESS_TOKEN"응답에서 scope에 androidpublisher가 포함되어 있는지 확인한다.
API 호출 테스트
# 환불 API 호출 예시
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
"https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.mygame.app/orders/GPA.1234-5678-9012:refund"7. 주의사항
토큰 캐싱
- Access Token은 보통 1시간 유효
- 매 요청마다 새로 발급하지 않고 캐싱해서 사용
- Google.Apis 패키지는 자동으로 토큰을 갱신해줌
Scope 명시
- JWT 생성 시 필요한 scope만 지정
- 과도한 권한은 보안 위험
마무리
Google API 호출을 위한 OAuth 2.0 인증 체계를 정리했다.
정리:
- Service Account: 서버 to 서버 통신용 특별 계정
- JWT Bearer Flow: Private Key로 서명한 JWT → Access Token
- Scope 지정: 필요한 API 권한만 요청
- 토큰 캐싱: 1시간 유효, 재발급 최소화
Google Play Developer API뿐 아니라 Google Cloud의 다른 API들도 같은 방식으로 호출할 수 있다. Cloud Storage, BigQuery 등도 동일한 인증 체계를 사용한다.
Loading comments...