Technical Justification Document for Google API Scopes Usage
Introduction
Our SMS (School Management System) integrates Google Classroom to enhance our educational delivery by leveraging Google's robust virtual classroom features. This integration allows teachers to seamlessly create, manage, and interact with their classes directly through our platform.
Scope Requirements
We request the following Google API scopes:
-
https://www.googleapis.com/auth/classroom.courses: Allows our application to create, read, and update courses on behalf of a teacher.
-
https://www.googleapis.com/auth/classroom.rosters: Enables our system to manage course rosters by inviting students to courses.
These scopes are crucial for providing a fluid and integrated experience for our users, enabling them to manage their courses and students within a single interface without needing to switch between our SMS and Google Classroom.
Code Implementation
GoogleOAuthConfig Module:
This module initializes the authorization flow with necessary scopes, ensuring secure storage of tokens.
Public Module GoogleOAuthConfig
Public Function InitializeFlow() As GoogleAuthorizationCodeFlow
Dim clientSecrets = New ClientSecrets() With {
.ClientId = "client-id.apps.googleusercontent.com",
.ClientSecret = "client-secret"
}
Dim scopes = New List(Of String) From {
ClassroomService.Scope.ClassroomCourses,
ClassroomService.Scope.ClassroomRosters
}
Dim dataStorePath = HttpContext.Current.Server.MapPath("~/App_Data/GoogleApiAuth")
If Not Directory.Exists(dataStorePath) Then
Directory.CreateDirectory(dataStorePath)
End If
Return New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer With {
.DataStore = New FileDataStore(dataStorePath, True),
.Scopes = scopes,
.ClientSecrets = clientSecrets
})
End Function
End Module
GoogleClassroomService Class:
Handles interactions with Google Classroom, including course creation and student invitations.
Public Class GoogleClassroomService
Private ReadOnly Service As ClassroomService
Public Sub New(credential As UserCredential)
Service = New ClassroomService(New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "MSJ Google Classroom API"
})
End Sub
Public Async Function CreateCourseAsync(courseName As String, sectionName As String) As Task(Of CourseDetails)
Dim newCourse = New Course With {
.Name = courseName,
.Section = sectionName,
.OwnerId = "me"
}
Dim createdCourse = Await Service.Courses.Create(newCourse).ExecuteAsync()
Return New CourseDetails With {
.Id = createdCourse.Id,
.AlternateLink = createdCourse.AlternateLink
}
End Function
Public Async Function InviteStudentsToCourse(courseId As String, studentEmails As List(Of String)) As Task
For Each email In studentEmails
Dim invitation = New Invitation() With {
.UserId = email,
.CourseId = courseId,
.Role = "STUDENT"
}
Await Service.Invitations.Create(invitation).ExecuteAsync()
Next
End Function
End Class
AuthController Overview
The AuthController is responsible for initiating and completing the OAuth 2.0 flow. It handles user redirection to Google's authentication services, receives the authorization code, and exchanges it for access tokens. This controller ensures that the application securely acquires the necessary permissions to operate on behalf of the user.
Code Implementation
Here's the detailed implementation of the AuthController, including methods for initiating the login process and handling the callback from Google:
<HttpGet>
<Route("api/auth/google-login")>
Public Async Function GoogleLogin(state As String) As Task(Of IHttpActionResult)
Dim flow = GoogleOAuthConfig.InitializeFlow()
Dim uri = Url.Link("DefaultApi", New With {.controller = "Auth", .action = "GoogleCallback"})
Dim result = Await New AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync("user", CancellationToken.None)
Dim authorizationUrl = flow.CreateAuthorizationCodeRequest(uri)
authorizationUrl.State = state ' Include the state parameter
Return Redirect(authorizationUrl.Build())
End Function
<HttpGet>
<Route("api/v2/auth/googleCallback")>
Public Async Function GoogleCallback(code As String, state As String) As Task(Of IHttpActionResult)
If String.IsNullOrEmpty(code) Then
Return Content(HttpStatusCode.BadRequest, "Error retrieving authorization code.")
End If
' Base64 decode and then deserialize the state parameter
Dim base64EncodedBytes As Byte() = Convert.FromBase64String(state)
Dim decodedState As String = System.Text.Encoding.UTF8.GetString(base64EncodedBytes)
Dim stateParams As Dictionary(Of String, String) = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(decodedState)
Dim classroomName As String = $"{stateParams("subjectName")} - {stateParams("classId")}"
Dim sectionName As String = stateParams("sectionName")
Dim classroomId As String = stateParams("classroomId")
Dim sn As String = stateParams("sn")
Dim subjectName As String = stateParams("subjectName")
Dim classId As String = stateParams("classId")
Dim classroomURL As String = Nothing
Try
Dim flow = GoogleOAuthConfig.InitializeFlow()
Dim tokenResponse = Await flow.ExchangeCodeForTokenAsync("user", code, Url.Link("DefaultApi", New With {.controller = "Auth", .action = "GoogleCallback"}), CancellationToken.None)
Dim credential = New UserCredential(flow, "user", tokenResponse)
' Use the Google Classroom Service to interact with the Google Classroom API
Dim ClassroomService = New GoogleClassroomService(credential)
Dim ClassroomCourseDetails = Await ClassroomService.CreateCourseAsync(classroomName, sectionName, subjectName, classId)
classroomURL = ClassroomCourseDetails.AlternateLink
Await ClassroomService.UpdateGoogleClassroomLink(sn, classroomId, ClassroomCourseDetails.AlternateLink)
Dim studentEmails = ClassroomService.GetStudentEmails(sn, classroomId)
'Await ClassroomService.AddStudentsToCourse(ClassroomCourseDetails.Id, studentEmails)
Await ClassroomService.InviteStudentsToCourse(ClassroomCourseDetails.Id, studentEmails)
Return Redirect(ClassroomCourseDetails.AlternateLink)
'Await classroomManager.AddStudentsToCourse(courseId, New List(Of String) {"student1@example.com", "student2@example.com"})
'Return Ok("Google authentication successful and classroom created.")
Catch ex As Exception
'Return Content(HttpStatusCode.InternalServerError, $"An error occurred: {ex.Message}")
Return Redirect(classroomURL)
End Try
End Function
Explanation of the AuthController Functionality
-
GoogleLogin Method: Initiates the OAuth 2.0 process by redirecting the user to Google's authentication page. It constructs the authorization URL with the appropriate scopes and state parameter for CSRF protection.
-
GoogleCallback Method: Handles the callback from Google. It receives the authorization code and exchanges it for an access token and refresh token. It handles errors and ensures that the application can securely use the token to make API requests on behalf of the authenticated user.
Detailed Explanation of the Client-Side Process
Here’s a step-by-step breakdown of what happens in the client-side function $scope.createGoogleClassroom, which manages interaction with Google Classroom:
1. Preparation of State Object
The function begins by preparing a stateObject containing necessary identifiers and names associated with the classroom. This object includes:
-
classId: Identifier for the class.
-
subjectName: Name of the subject.
-
sectionName: A combination of the year and term.
-
classroomId: A unique identifier for the classroom within your system.
-
sn: Presumably a session or school identifier.
This state object is used to maintain context and carry essential classroom and session identifiers through the OAuth flow.
2. Decision Based on Existing Classroom Link
The function checks if a GoogleClassroomLink already exists for the classroom:
-
If it exists, the URL is set to this link, allowing direct access to the classroom.
-
If it does not exist, the function prepares to initiate the OAuth flow to allow the user to create or manage the classroom settings.
3. State Object Encoding
For new classroom management:
-
The stateObject is converted into a JSON string.
-
This JSON string is then encoded into Base64 format to ensure safe transmission through URL parameters.
-
The encoded string is URL-encoded again to ensure it adheres to URL standards, avoiding issues with special characters.
4. Opening the URL
- A new browser tab or window is opened with the constructed URL, either directing the user directly to the classroom or to the OAuth login process.
Sample Client-Side Function Adjusted for Clarity
$scope.createGoogleClassroom = function(classroom) {
var stateObject = {
classId: classroom.classid,
subjectName: classroom.subjectname,
sectionName: classroom.year + ' (' + classroom.term + ')',
classroomId: classroom.id,
sn: classroom.sn // Ensure 'sn' is defined or passed appropriately
};
var url = classroom.GoogleClassroomLink;
if (!classroom.GoogleClassroomLink) {
var stateJson = JSON.stringify(stateObject);
var stateBase64 = btoa(stateJson);
url = '/api/auth/google-login?state=' + encodeURIComponent(stateBase64);
}
$scope.changePeriod(); //reload the classrooms
window.open(url, '_blank');
};
Comments
0 comments
Please sign in to leave a comment.