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

使用C#访问Google Classroom作业AddOnAttachment时遇权限错误

Google Classroom AddOnAttachment操作权限不足问题

以教师账号调用Google Classroom API时,课程、作业(CourseWork)、公告(Announcement)的访问/创建均正常,但AddOnAttachment的列表查询、创建操作全部抛出权限不足错误:

The service classroom has thrown an exception. HttpStatusCode is Forbidden. Request had insufficient authentication scopes.

错误触发位置:

  • 查询AddOnAttachment列表时:foreach (AddOnAttachment item in ccarlr.Execute().AddOnAttachments)
  • 创建AddOnAttachment时:cwaoa.Execute()

现有权限配置

当前代码中配置的OAuth2权限范围如下:

static string[] Scopes = { 
    ClassroomService.Scope.ClassroomCourses, 
    ClassroomService.Scope.ClassroomCourseworkmaterials,
    ClassroomService.Scope.ClassroomCourseworkMe, 
    ClassroomService.Scope.ClassroomCourseworkStudents, 
    ClassroomService.Scope.ClassroomCourseworkStudentsReadonly, 
    ClassroomService.Scope.ClassroomGuardianlinksStudents,
    ClassroomService.Scope.ClassroomAnnouncements, 
    ClassroomService.Scope.ClassroomCourseworkMe, 
    ClassroomService.Scope.ClassroomProfileEmails, 
    ClassroomService.Scope.ClassroomProfilePhotos,
    ClassroomService.Scope.ClassroomPushNotifications, 
    ClassroomService.Scope.ClassroomRosters, 
    ClassroomService.Scope.ClassroomStudentSubmissionsMeReadonly,
    ClassroomService.Scope.ClassroomStudentSubmissionsStudentsReadonly, 
    ClassroomService.Scope.ClassroomTopics
};

相关代码示例

完整Classroom类代码

public class Classroom
{
    static string[] Scopes = { ClassroomService.Scope.ClassroomCourses, ClassroomService.Scope.ClassroomCourseworkmaterials,
    ClassroomService.Scope.ClassroomCourseworkMe, ClassroomService.Scope.ClassroomCourseworkStudents, ClassroomService.Scope.ClassroomCourseworkStudentsReadonly, ClassroomService.Scope.ClassroomGuardianlinksStudents,
    ClassroomService.Scope.ClassroomAnnouncements, ClassroomService.Scope.ClassroomCourseworkMe, ClassroomService.Scope.ClassroomProfileEmails, ClassroomService.Scope.ClassroomProfilePhotos,
    ClassroomService.Scope.ClassroomPushNotifications, ClassroomService.Scope.ClassroomRosters, ClassroomService.Scope.ClassroomStudentSubmissionsMeReadonly,
    ClassroomService.Scope.ClassroomStudentSubmissionsStudentsReadonly, ClassroomService.Scope.ClassroomTopics};
    static string ApplicationName = "ControlGoogleMCV";

    public GDrive_baj GDrive_bajD = new GDrive_baj();
public void Main()
{
    ClientSecrets secrets = new ClientSecrets()
    {
        ClientId = "________________",
        ClientSecret = "__________________________"
    };

    UserCredential credential;

    using (var stream =
      new FileStream(Form1.ObtenerAtributo("rutalocaljson"), FileMode.Open, FileAccess.Read))
    {
  
        string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
        credPath = Path.Combine(credPath, ".credentials/", System.Reflection.Assembly.GetExecutingAssembly().GetName().FullName, "paraclassroom");
        credential = Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(secrets,
            Scopes,
            "_________",
            CancellationToken.None,
            new FileDataStore(credPath, true)).Result;
        Console.WriteLine("Credential file saved to: " + credPath);
    }

    // Create Classroom API service.
    var service = new ClassroomService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = ApplicationName,
    });

    // Define request parameters.
    CoursesResource.ListRequest request = service.Courses.List();
    request.PageSize = 10;

    try
    {
        CoursesResource.ListRequest lr = new CoursesResource.ListRequest(service);

        string cadbusname = Interaction.InputBox("Cadena de búsqueda en el atributo ''name'' del curso. ''Cancelar'' no " +
            "dará resultado; ''Aceptar'' inicia la búsqueda", "Cadena de búsqueda en el nombre del curso", "Transcripción de la música flamenca 2024/2025"
        );
        if (cadbusname.Length > 0) {
            foreach (Course c in lr.Execute().Courses)
            {
                if (c.Name.Contains(cadbusname))
                {
                    string cadbusnametarea = Interaction.InputBox("Cadena de búsqueda en el atributo ''name'' de la tarea. ''Cancelar''" +
   "no dará resultado; ''Aceptar'' inicia la búsqueda", "Cadena de búsqueda en el nombre de la tarea", "Ejercicio 8.");
                    CoursesResource.CourseWorkResource.ListRequest cwrlr = new CoursesResource.CourseWorkResource.ListRequest(service, c.Id);

                    foreach (CourseWork cwr in cwrlr.Execute().CourseWork) 
                    {
                        if (cwr.Title.Contains(cadbusnametarea))
                        {                           
                            string idCur = cwr.Id;
                            var cwrmat = cwr.Materials;
                  
                            foreach (var item in cwrmat)
                            {
                                if (item.DriveFile != null)
                                {
                                    string tx = "El archivo se ha subido desde Google Drive. El thumbonailurl es : " + item.DriveFile.DriveFile.ThumbnailUrl +
                                         ". El enlace alternativo es: " + item.DriveFile.DriveFile.AlternateLink + ". El título es: " + item.DriveFile.DriveFile.Title;
                                }
                                if (item.Link != null)
                                {
                                    string tx = "Se trata de un enlace. La url es " + item.Link.Url + ". El thumbonailurl es : " + item.Link.ThumbnailUrl +
                                        ". El título es: " + item.Link.Title;
                                }
                            }

CoursesResource.CourseWorkResource.AddOnAttachmentsResource.ListRequest ccarlr =                               new CourseWorkResource.AddOnAttachmentsResource.ListRequest(service, c.Id, idCur);
                            foreach (AddOnAttachment item in ccarlr.Execute().AddOnAttachments)
                            {
                                string nn = item.ItemId;  
                            }
                        }
                    }
                }
            }
        };               
    }
    catch (Exception e) 
    {
        string ff = e.Message;
    }
}
}

可正常执行的公告创建代码

// 公告可正常创建
Google.Apis.Classroom.v1.Data.Announcement anu = new Announcement();
anu.Text = "El cuerpo del anuncio para el curso Prueba";
Google.Apis.Classroom.v1.CoursesResource.AnnouncementsResource.CreateRequest cr = new
    CoursesResource.AnnouncementsResource.CreateRequest(service, anu, c.Id);
cr.Execute();

失败的AddOnAttachment创建代码

// 权限不足
Google.Apis.Classroom.v1.Data.AddOnAttachment addo = new AddOnAttachment();
addo.Title = "El título del add-on attachment para la tarea Prueba1";
Google.Apis.Classroom.v1.CoursesResource.CourseWorkResource.AddOnAttachmentsResource.CreateRequest cwaoa = new
   CourseWorkResource.AddOnAttachmentsResource.CreateRequest(service, addo, c.Id, idCur);
cwaoa.Execute(); // 此处触发错误

创建CourseWork后立即创建AddOnAttachment仍失败

// CourseWork可正常创建,但创建其AddOnAttachment时仍触发权限错误
Google.Apis.Classroom.v1.Data.CourseWork cw = new CourseWork();
cw.Title = "New courseWork for the course";
cw.WorkType = "Assignment";
cw.State = "PUBLISHED";
String idCW = "";
Google.Apis.Classroom.v1.CoursesResource.CourseWorkResource.CreateRequest cwre = 
    new CourseWorkResource.CreateRequest (service, cw, c.Id);
cwre.Execute();
foreach (Google.Apis.Classroom.v1.Data.CourseWork item in cwrlr.Execute().CourseWork)
{
 if (item.Title == cw.Title)
    {
        idCW = item.Id;
        break;
    }
}

Google.Apis.Classroom.v1.Data.AddOnAttachment addoA = new AddOnAttachment();
addoA.Title = "The title of the add-on attachment for the task created";
Google.Apis.Classroom.v1.CoursesResource.CourseWorkResource.AddOnAttachmentsResource.CreateRequest cwaoa = new
   CourseWorkResource.AddOnAttachmentsResource.CreateRequest(service, addoA, c.Id, idCW);
cwaoa.Execute(); // 此处触发错误

解决方案

核心原因

当前配置的OAuth2权限范围中,缺少AddOnAttachment操作的专用权限,这类权限和普通CourseWork权限是分离的。

需要添加的权限

针对教师账号,需添加:

  • ClassroomService.Scope.ClassroomAddOnAttachmentsTeacher:支持教师账号创建、读取、修改AddOnAttachment

操作步骤

  1. 更新权限范围数组:修改Scopes变量,添加上述权限
    static string[] Scopes = { 
        // 保留原有所有权限
        ClassroomService.Scope.ClassroomAddOnAttachmentsTeacher // 新增此权限
    };
    
  2. 删除本地凭据文件:找到代码中credPath指定的路径(通常是用户目录/.credentials/xxx/paraclassroom),删除该目录下的所有文件。旧凭据的权限范围不包含新增权限,必须重新授权。
  3. 重新运行代码:此时会触发OAuth授权流程,重新获取包含新权限的凭据。

内容的提问来源于stack exchange,提问作者Manuel Cera Vera

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 19:17:32