Search code examples
pythongoogle-classroom

Either 403 or Scope has changed error in google classroom, what do I do?


I am trying to get grades from a class's assignments but if I try to call the service.courses().courseWork().list(courseId=course['id']).execute() it gives me

Warning: Scope has changed from "https://www.googleapis.com/auth/classroom.courses.readonly https://www.googleapis.com/auth/classroom.coursework.me.readonly https://www.googleapis.com/auth/classroom.rosters.readonly" to "https://www.googleapis.com/auth/classroom.student-submissions.me.readonly https://www.googleapis.com/auth/classroom.courses.readonly https://www.googleapis.com/auth/classroom.rosters.readonly".

and if I change the scopes to the recommended ones, I get this.

googleapiclient.errors.HttpError: <HttpError 403 when requesting https://classroom.googleapis.com/v1/courses/[id]/courseWork?alt=json returned "The caller does not have permission". Details: "The caller does not have permission">

my code:

from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

import warnings

# Set up the OAuth2 flow
flow = InstalledAppFlow.from_client_secrets_file(
    'client_secret.json',
    scopes=[
        "https://www.googleapis.com/auth/classroom.coursework.me.readonly",
        "https://www.googleapis.com/auth/classroom.rosters.readonly",
        "https://www.googleapis.com/auth/classroom.courses.readonly"
    ])


flow.run_local_server(port=0)

# Get the OAuth2 credentials
creds = flow.credentials

# You can then use the credentials to authenticate requests to the Google API
service = build('classroom', 'v1', credentials=creds)


# Get a list of all the courses that you are a part of
courses = service.courses().list().execute()

# Loop through the courses and print the course names and IDs
for course in courses['courses']:
    print(f"Course Name: {course['name']}")
    print(f"Course ID: {course['id']}")

    cw = service.courses().courseWork().list(courseId=course['id']).execute()
    
    for assig in cw['courseWork']:
        print(assig)

Solution

  • This is a known issue and I would encourage you to upvote or comment on the corresponding Google IssueTracker ticket https://issuetracker.google.com/issues/78592659.

    In the OAuth protocol, servers don't need to return the exact scopes requested. This is the case here as well as in other (non-Google) APIs. The server is returning the https://www.googleapis.com/auth/classroom.student-submissions.me.readonly scope because it is equivalent to the the client-requested https://www.googleapis.com/auth/classroom.coursework.me.readonly scope.

    However, a dependency in the Python client library (oauthlib) is treating this scope difference as an error instead of a warning.

    If it's acceptable, one solution is to set the OAUTHLIB_RELAX_TOKEN_SCOPE variable to True in your environment to suppress the error, as described in this answer.

    My guess for the reason you are getting a 403 when you change the client-requested scope is that you may not have updated the scopes in your GCP projectwhen you changed them in your code.