You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全使用Service Account创建Google Calendar邀请(无需全域权限)

问题描述

使用企业Google Workspace账号创建了Service Account,需要通过Python自动化创建日历邀请。已将服务账号邮箱添加到日历共享人员列表中,调用service.calendarList().list().execute()可正常获取日历,但创建邀请时失败,报错:

googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/calendar/v3/calendars/xxxxxx%group.calendar.google.com/events?alt=json returned "You need to have writer access to this calendar.". Details: "[{'domain': 'calendar', 'reason': 'requiredAccessLevel', 'message': 'You need to have writer access to this calendar.'}]"

查阅文档得知需为服务账号配置全域权限,但公司因安全问题不允许此权限。想咨询是否无需配置全域权限,仅让服务账号模拟创建它的父账号即可实现功能?

以下是使用的完整代码:

from google.oauth2 import service_account
from googleapiclient.discovery import build


class GoogleCalendar:
    SCOPES = [
        "https://www.googleapis.com/auth/calendar",
        "https://www.googleapis.com/auth/calendar.events",
    ]

    def __init__(self, credentials, calendar_id) -> None:
        credentials = service_account.Credentials.from_service_account_file(
            credentials, scopes=self.SCOPES
        )
        self.service = build("calendar", "v3", credentials=credentials)
        self.id = calendar_id

    def get_calendar_list(self):
        return self.service.calendarList().list().execute()

    def add_calendar(self):
        entry = {"id": self.id}
        return self.service.calendarList().insert(body=entry).execute()

    def create_invite(self):
        event = {
            "summary": "Google I/O 2015",
            "location": "800 Howard St., San Francisco, CA 94103",
            "description": "A chance to hear more about Google's developer products.",
            "start": {
                "dateTime": "2023-09-16T09:00:00-07:00",
                "timeZone": "America/Los_Angeles",
            },
            "end": {
                "dateTime": "2023-09-16T17:00:00-07:00",
                "timeZone": "Indian/Mauritius",
            },
            "attendees": [{"email": "myemail@domain.com"}],
        }

        event = self.service.events().insert(calendarId=self.id, body=event).execute()

work_cal_id = "xxxxx@group.calendar.google.com"

cal = GoogleCalendar(
credentials="work.json", 
calendar_id=work_cal_id
)

cal.add_calendar()

print(cal.get_calendar_list())

cal.create_invite()
解决方案

可以通过让服务账号**模拟创建它的父账号(你的企业Google Workspace账号)**实现功能,无需配置全域权限,只需管理员为服务账号配置针对特定用户的域范围委派权限即可,具体操作如下:

  1. 管理员配置域范围委派

    • 登录Google Workspace管理员控制台,定位到目标服务账号
    • 启用「域范围委派」,并添加所需的OAuth2范围:
      • https://www.googleapis.com/auth/calendar
      • https://www.googleapis.com/auth/calendar.events
    • 此配置仅允许服务账号模拟域内用户,只需指定它模拟你自己的企业账号即可,无需授予全域权限,安全可控。
  2. 修改Python代码
    在创建服务账号凭证时,添加subject参数指定要模拟的父账号邮箱(即拥有该日历写入权限的你的企业账号):

from google.oauth2 import service_account
from googleapiclient.discovery import build


class GoogleCalendar:
    SCOPES = [
        "https://www.googleapis.com/auth/calendar",
        "https://www.googleapis.com/auth/calendar.events",
    ]

    def __init__(self, credentials, calendar_id, impersonate_email) -> None:
        # 添加subject参数,指定要模拟的用户邮箱
        credentials = service_account.Credentials.from_service_account_file(
            credentials, scopes=self.SCOPES, subject=impersonate_email
        )
        self.service = build("calendar", "v3", credentials=credentials)
        self.id = calendar_id

    # 其余方法保持不变
    def get_calendar_list(self):
        return self.service.calendarList().list().execute()

    def add_calendar(self):
        entry = {"id": self.id}
        return self.service.calendarList().insert(body=entry).execute()

    def create_invite(self):
        event = {
            "summary": "Google I/O 2015",
            "location": "800 Howard St., San Francisco, CA 94103",
            "description": "A chance to hear more about Google's developer products.",
            "start": {
                "dateTime": "2023-09-16T09:00:00-07:00",
                "timeZone": "America/Los_Angeles",
            },
            "end": {
                "dateTime": "2023-09-16T17:00:00-07:00",
                "timeZone": "Indian/Mauritius",
            },
            "attendees": [{"email": "myemail@domain.com"}],
        }

        event = self.service.events().insert(calendarId=self.id, body=event).execute()

# 替换为你的实际信息
work_cal_id = "xxxxx@group.calendar.google.com"
your_work_email = "your-email@domain.com"  # 你的企业账号邮箱

cal = GoogleCalendar(
    credentials="work.json", 
    calendar_id=work_cal_id,
    impersonate_email=your_work_email
)

cal.add_calendar()
print(cal.get_calendar_list())
cal.create_invite()
  1. 权限验证
    • 确保你的父账号(被模拟的账号)对目标日历拥有写入权限
    • 服务账号通过模拟该账号,将继承其日历操作权限,从而成功创建邀请

这种方式的核心是服务账号以你的身份操作日历,而非自身身份,既避免了给服务账号单独分配日历权限的问题,也规避了全域权限的安全风险。

内容的提问来源于stack exchange,提问作者Mervin Hemaraju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:53:13