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

C#开发Google Workspace共享日历跨用户删除事件权限问题

解决Google Workspace共享日历删除事件的权限问题

问题分析

你遇到的unauthorized_client错误发生在令牌刷新阶段,结合代码和场景来看,核心原因集中在认证逻辑的分散性和权限一致性问题:

  1. 不同业务方法使用了独立的权限范围集合和凭据存储位置,导致令牌缺少必要权限或无法正确刷新
  2. 重复执行认证流程,覆盖或调用了不完整的凭据
  3. 共享日历的权限设置可能未赋予用户修改他人事件的权限(认证修复后仍需确认)

解决方案

1. 统一权限范围和凭据存储

将所有需要的权限合并为全局列表,并在所有认证调用中使用相同的存储位置,确保凭据包含完整权限:

首先定义全局权限集合:

public static class CustomApplicationContext
{
    // 合并所有业务所需的权限范围
    public static readonly string[] AllScopes = new[]
    {
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/gmail.send",
        "https://www.googleapis.com/auth/gmail.settings.basic",
        "https://www.googleapis.com/auth/calendar",
        "https://www.googleapis.com/auth/calendar.events",
        "https://www.googleapis.com/auth/calendar.app.created"
    };
    
    // 其他全局变量保持不变
    public static UserCredential credential;
    public static string mySignature;
    public static string myEMail;
    public static string AWAY;
    // ...
}

2. 重构认证逻辑,仅执行一次

在应用启动时完成认证,避免在每个业务方法中重复调用认证流程:

修改DoAuthenticationAsync方法:

public static async Task<bool> DoAuthenticationAsync()
{
    ClientSecrets secrets;

    using (var strm = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
    {
        secrets = GoogleClientSecrets.FromStream(strm).Secrets;
    }

    try
    {
        // 使用全局权限集合和统一的存储名称
        CustomApplicationContext.credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
            secrets,
            CustomApplicationContext.AllScopes,
            "user", // 确保用户标识一致
            CancellationToken.None,
            new FileDataStore("Time.Tracker.Desktop", false));

        var init = new BaseClientService.Initializer
        {
            HttpClientInitializer = CustomApplicationContext.credential
        };

        var gmailSvc = new GmailService(init);
        var result = await gmailSvc.Users.Settings.SendAs.List("me").ExecuteAsync();

        foreach (var itm in result.SendAs)
        {
            if (itm.IsPrimary.HasValue && itm.IsPrimary.Value)
            {
                CustomApplicationContext.mySignature = itm.Signature;
                CustomApplicationContext.myEMail = itm.SendAsEmail;
                break;
            }
        }
    }
    catch (Exception ex)
    {
        try
        {
            DBErrorLogger.LogError(ex, MethodBase.GetCurrentMethod().Name, 14, CustomApplicationContext.currentUser.Appuserid);
        }
        catch { }
        throw;
    }

    return true;
}

3. 修改事件操作方法,重用已认证凭据

移除方法内的重复认证逻辑,直接使用全局已认证的凭据:

获取事件方法:

public static async Task<Event> GetEvent(string id)
{
    try
    {
        if (CustomApplicationContext.credential == null)
        {
            throw new InvalidOperationException("请先完成认证");
        }

        var init = new BaseClientService.Initializer
        {
            HttpClientInitializer = CustomApplicationContext.credential
        };

        var calSvc = new CalendarService(init);
        var evt = await calSvc.Events.Get(CustomApplicationContext.AWAY, id).ExecuteAsync();

        return evt;
    }
    catch (Exception ex)
    {
        try
        {
            DBErrorLogger.LogError(ex, MethodBase.GetCurrentMethod().Name, 14, CustomApplicationContext.currentUser.Appuserid);
        }
        catch { }
        throw;
    }
}

删除事件方法:

public static async Task<bool> RemoveEvent(string id)
{
    try
    {
        if (CustomApplicationContext.credential == null)
        {
            throw new InvalidOperationException("请先完成认证");
        }

        var init = new BaseClientService.Initializer
        {
            HttpClientInitializer = CustomApplicationContext.credential
        };

        var calSvc = new CalendarService(init);
        await calSvc.Events.Delete(CustomApplicationContext.AWAY, id).ExecuteAsync();

        return true;
    }
    catch (Exception ex)
    {
        try
        {
            DBErrorLogger.LogError(ex, MethodBase.GetCurrentMethod().Name, 14, CustomApplicationContext.currentUser.Appuserid);
        }
        catch { }
        throw;
    }
}

4. 确认共享日历权限设置

即使修复了认证问题,仍需确保:

  • 共享日历所有者已为操作用户(或其所在组)授予修改事件或管理日历的权限(仅"创建事件"权限不足以删除他人创建的事件)
  • 在Google Workspace管理后台或日历界面中,检查该用户的共享权限级别

5. 清理旧凭据

由于之前使用了不同的存储位置,建议删除本地存储的旧凭据文件,重新进行认证以确保新凭据包含所有必要权限:

  • Windows: %APPDATA%\Google.Apis.Auth
  • macOS: ~/Library/Application Support/Google.Apis.Auth

内容的提问来源于stack exchange,提问作者Shane Brodie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:14:50