C#开发Google Workspace共享日历跨用户删除事件权限问题
解决Google Workspace共享日历删除事件的权限问题
问题分析
你遇到的unauthorized_client错误发生在令牌刷新阶段,结合代码和场景来看,核心原因集中在认证逻辑的分散性和权限一致性问题:
- 不同业务方法使用了独立的权限范围集合和凭据存储位置,导致令牌缺少必要权限或无法正确刷新
- 重复执行认证流程,覆盖或调用了不完整的凭据
- 共享日历的权限设置可能未赋予用户修改他人事件的权限(认证修复后仍需确认)
解决方案
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
相关产品推荐
相关产品推荐

