Using LTI Tools in Google Classroom

According to the the EdNet Insight report, Educational Technology Trends: State of the K-12 Market 2016”, 67% of US school districts are using Google Classroom as an LMS. Unfortunately, Google does not yet support the IMS LTI standards for interoperability. In this post I walk through a proof of concept (POC) that converts a request coming from Google Classroom into an LTI request to a Tool.

Here’s an overview:

  1. Use a Classroom share button to add a specially formatted link into the course. The link points to a .NET Controller (Gc2LtiController) that will create the LTI request and post it to the LTI Tool.
  2. When the Gc2LtiController  receives the get request from Google Classroom, it uses several Google APIs to get the data needed to form the LTI request.
  3. The Gc2LtiController also needs to determine which key and secret to use to sign the request. I imagine this can be done using the information collected above, but since this is all happening in a browser session, you could also prompt the user for a code or another clue to find the right key and secret.
  4. The Gc2LtiController then signs the LTI request and posts it to the Tool.

 

I used Visual Studio Community 2017 and .NET Core 2.0 for everything in this POC, and I’ve put the entire solution up in github. This post walks through some parts of the POC and explains how they work.

Share to Classroom

The Classroom share button needs to insert a link to the Gc2LtiController and pass the the URL of the actual LTI Tool. Something like this:

https://localhost:44319/gc2lti?url=https://lti.tools/test/tp.php

Google’s share button will create a Coursework resource with this link. The Gc2LtiController will use the request URL to look up the matching Coursework resource. If this link has been assigned more than once to the same course, then Gc2LtiController will use the most recent.

That is not a problem if your Tool does not treat each assignment uniquely. For example, if your Tool is a game without bookmarking or scores, then it does not matter which Coursework resource it was launched from.

But if your Tool needs to differentiate between multiple assignments of the same Tool (for example, if your Tool uses the LTI resource_link_id then it probably does)  then the link URL will need something to differentiate each assignment. I couldn’t find a way to modify the link URL with each click, but it was pretty easy to add a nonce to the link URL when the Classroom share button is rendered so it looks like this:

https://localhost:44319/gc2lti/9d362ba3feac48ffaa0cd1f1e2cd6e1c?url=http://lti.tools/test/tp.php

The data-url attribute of the Classroom share button gets the URL from the ShareUrl property of the PageModel,
classroom-share-button

The ShareUrl property is calculated when the page is rendered. Skip the nonce if you don’t need it, and use a URL to your actual LTI tool. You can use http://lti.tools/test/tp.php. It is a very handle Tool for testing that validates the LTI request and dumps the parameters.
resource-code

When you click on the Classroom share button and follow the prompts, you will end up with something like this,
poc-lti-tool

The preview image is broken because Google’s screen thumbnail capture service can’t access localhost. The Gc2LtiController will recognize when Google is trying to get a thumbnail image and you can supply whatever you want in return.

Now let’s walk through the Gc2LtiController.

Gc2LtiController

The Gc2LtiController (Google Classroom to LTI) will receive a get request when the teacher or a student in the classroom clicks on the Link. Gc2LtiController uses clues in the request (e.g. the Referer header), the Google Classroom API, and the Google Admin Directory API to fill in most of the required and useful parameters of an LTI request.

The only required parameters this POC version of Gc2LtiController cannot determine is the oauth_consumer_key and secret. I’ve hard coded the key and secret for https://lti.tools/test/tp.php. A real implementation will need to determine the key and secret based on the information gathered from Google, or by asking the user to supply a clue such as a code.

I found the Google Classroom API .NET Quickstart and the web application section of the OAuth 2.0 quide useful when writing Gc2LtiController. I ran into one glitch: Google’s quickstarts and .NET API libraries were created before .NET Core 2.0. This was really only an issue with the Google.Apis.Auth.Mvc library. For this POC, I used @buzallen‘s replacement he calls Google.Apis.Auth.AspMvcCore.

Prerequisites

To run this POC, you will need several things:

  • Visual Studio 2017.
  • Access to the internet and a web browser.
  • A Google account (for you as the developer of the project).
  • A second Google account with Google Classroom enabled for testing. A G Suite for Education account is preferred, but this works with other account types with some degradation.
  • Download @buzallen‘s Google.Apis.Auth.Mvc replacement he calls Google.Apis.Auth.AspMvcCore. You will probably need to fix the Google.Apis.Auth.AspMvcCore project reference in the gc2lti-poc solution so that it points where you downloaded the project.
  • Enable the Classroom API and the Admin SDK using the Google Developers Console. See Google’s Classroom Quickstart for details.
  • Create an OAuth Client ID for a web application, also using the Google Developers Console.
  • Add https://localhost:44319/AuthCallback as an authorized Redirect URL to the Client ID.
  • Store the Client ID and Secret for the Gc2LtiController using the Secret Manager:
    1. Right click on the gc2lti project and select Manage User Secrets.
    2. Store your Client ID and Secret in the secrets.json file.
      {
        "Authentication:Google:ClientId": "YOUR CLIENT ID",
        "Authentication:Google:ClientSecret": "YOUR SECRET"
      }
      

You should be able to run both the catalog and gc2lti projects now. The rest of this post walks through some of the interesting bits of Gc2LtiController.

The Gc2LtiController.Index Action

Links to Gc2LtiController looks like one of these:

https://localhost:44319/gc2lti?url=http://lti.tools/test/tp.php
https://localhost:44319/gc2lti/9d362ba3feac48ffaa0cd1f1e2cd6e1c?url=http://lti.tools/test/tp.php

To allow either format, the route for the default (Index) action makes the nonce optional,

And since nonce has no value to Gc2LtiController by itself, there is no matching parameter. The full request URL (including the nonce if provided) is used to find a matching Coursework resource later.

Google calls the default (Index) Action many times for different purposes. The first time is to capture a thumbnail sized screenshot of your LTI Tool. This is called by one of Google’s servers (not through a browser), so it is not a good time to actually launch your tool. I suggest returning a generic page that looks nice.

The next time the Index Action is called, the teacher or student has clicked on the link hoping to launch the Tool. To form an LTI request, Gc2LtiController needs a bunch of information from Google. That means the controller needs authorization.

Google’s authorization flow redirects away and back to the Index Action 2 or 3 times during the authorization flow (3 the first time a user interacts with your copy of Gc2Lti so they can agree to allow you access). The first bit of data is collected we need is collected from these requests: the alternate link for the course,

Once the app is authorized, it’s time to start forming the LTI request,

The first set of information comes from the Google Classroom API,

And the second from the Google Directory API,

At this point the LTI request has all the user, context, and resource information. It’s time look up the key and secret, sign the request, and post it to your LTI Tool,

FillInUserAndPersonInfo

This is very straightforward because Google stores very LTI-like information in the Classroom UserProfile,

FillInContextAndResourceInfo

This one is a little more complex because we need to find the matching Course and Coursework. When the teacher or student clicks on the link and if the scheme is https, then Google will include the Course Alternative Link in the request Referer header. To find the matching course, we need to search the list of this user’s courses, looking for the one with the matching AlternativeLink,

Once we have the context_id (= Google Classroom Course ID), then we can search the Coursework for one with a Link that matching the Request URL. This code will find the first (most recent) Coursework with a matching URL. If the teacher manually created the link or used the Classroom Reuse feature, the matching Coursework might not be the one that was originally assigned with the Classroom share button,

FillInContextRoleInfo

Getting the context role is also pretty straightforward…just check to see if the current user is in the list of course teachers,

FillInPersonSyncInfo

Google has 3 ways for schools to associate SIS information with users:

  1. Google School Directory Sync for G Suite for Education customers.
  2. Google Cloud Directory Sync for G Suite customers.
  3. G Suite Bulk Account Update for G Suite customers.

There is no way to add syncing information to normal Google accounts (i.e. anyone@gmail.com).

All 3 ways store the syncing information in the user’s Google Account as ExternalIds, which we can get to with the Google Directory API. The syncing id is stored in slightly different ways depending on the way it was captured,

What’s Next?

The next thing I’ll look at is how to handle LTI outcomes (and gradebook when that spec is public). I’m thinking the Gc2LtiController will need an outcomes endpoint that receives results from the LTI Tool and forwards them to Google Classroom using the Google Classroom API. I’m a little worried this may not be easy because of this statement in the documentation,

This entry was posted in Google, LTI and tagged , , . Bookmark the permalink.

One Response to Using LTI Tools in Google Classroom

  1. Pingback: Sending LTI Outcomes to Google Classroom | Learning Tools

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s