From 4d06580b8ebd07de28d7d5be253a045f45a4e04f Mon Sep 17 00:00:00 2001 From: jongfeel Date: Mon, 8 Jun 2026 15:48:53 +0900 Subject: [PATCH] Replace service account with OAuth2 user credentials for calendar attendee management Service accounts cannot invite attendees without Domain-Wide Delegation, which is unavailable for personal Google accounts. Switch to OAuth2 user credentials (stored as GitHub secrets) to add guests to calendar events. Co-Authored-By: Claude Sonnet 4.6 --- .../workflows/add-assignee-to-calendar.yml | 17 ++++-- scripts/get_calendar_oauth_token.py | 52 +++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 scripts/get_calendar_oauth_token.py diff --git a/.github/workflows/add-assignee-to-calendar.yml b/.github/workflows/add-assignee-to-calendar.yml index ec078cb..780c2ad 100644 --- a/.github/workflows/add-assignee-to-calendar.yml +++ b/.github/workflows/add-assignee-to-calendar.yml @@ -7,6 +7,8 @@ on: jobs: add-assignee: runs-on: ubuntu-latest + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true # 모각코 이슈 형식에만 실행 (예: "285th online meetup, 2026-05-02") if: ${{ contains(github.event.issue.title, 'online meetup,') }} @@ -21,7 +23,9 @@ jobs: - name: Add assignee as guest to calendar event env: - GOOGLE_SERVICE_ACCOUNT_KEY: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }} + GOOGLE_OAUTH2_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH2_CLIENT_ID }} + GOOGLE_OAUTH2_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH2_CLIENT_SECRET }} + GOOGLE_OAUTH2_REFRESH_TOKEN: ${{ secrets.GOOGLE_OAUTH2_REFRESH_TOKEN }} GOOGLE_CALENDAR_ID: ${{ secrets.GOOGLE_CALENDAR_ID }} ASSIGNEE_LOGIN: ${{ github.event.assignee.login }} ISSUE_NUMBER: ${{ github.event.issue.number }} @@ -30,7 +34,7 @@ jobs: run: | python - <<'PYTHON' import os, json, time, subprocess - from google.oauth2 import service_account + from google.oauth2.credentials import Credentials from googleapiclient.discovery import build login = os.environ['ASSIGNEE_LOGIN'] @@ -55,9 +59,12 @@ jobs: f"Add '{login}: ' to CALENDAR_EMAIL_MAP variable to enable.") exit(0) - key_info = json.loads(os.environ['GOOGLE_SERVICE_ACCOUNT_KEY']) - credentials = service_account.Credentials.from_service_account_info( - key_info, scopes=['https://www.googleapis.com/auth/calendar'] + credentials = Credentials( + token=None, + refresh_token=os.environ['GOOGLE_OAUTH2_REFRESH_TOKEN'], + client_id=os.environ['GOOGLE_OAUTH2_CLIENT_ID'], + client_secret=os.environ['GOOGLE_OAUTH2_CLIENT_SECRET'], + token_uri='https://oauth2.googleapis.com/token', ) service = build('calendar', 'v3', credentials=credentials) calendar_id = os.environ['GOOGLE_CALENDAR_ID'] diff --git a/scripts/get_calendar_oauth_token.py b/scripts/get_calendar_oauth_token.py new file mode 100644 index 0000000..010ef97 --- /dev/null +++ b/scripts/get_calendar_oauth_token.py @@ -0,0 +1,52 @@ +""" +One-time script to get Google Calendar OAuth2 refresh token. + +Prerequisites: + 1. Google Cloud Console에서 OAuth2 Client ID 생성 (유형: Desktop app) + 2. pip install google-auth-oauthlib + +Usage: + python get_calendar_oauth_token.py + +After running: + 출력된 세 값을 GitHub repo secrets에 추가: + - GOOGLE_OAUTH2_CLIENT_ID + - GOOGLE_OAUTH2_CLIENT_SECRET + - GOOGLE_OAUTH2_REFRESH_TOKEN +""" +import sys +from google_auth_oauthlib.flow import InstalledAppFlow + +SCOPES = ['https://www.googleapis.com/auth/calendar'] + + +def main(): + if len(sys.argv) != 3: + print("Usage: python get_calendar_oauth_token.py ") + sys.exit(1) + + client_id, client_secret = sys.argv[1], sys.argv[2] + + flow = InstalledAppFlow.from_client_config( + { + "installed": { + "client_id": client_id, + "client_secret": client_secret, + "redirect_uris": ["http://localhost"], + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + } + }, + SCOPES, + ) + + creds = flow.run_local_server(port=0) + + print("\n--- GitHub Secret Values ---") + print(f"GOOGLE_OAUTH2_CLIENT_ID: {client_id}") + print(f"GOOGLE_OAUTH2_CLIENT_SECRET: {client_secret}") + print(f"GOOGLE_OAUTH2_REFRESH_TOKEN: {creds.refresh_token}") + + +if __name__ == "__main__": + main()