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,

Posted in Google, LTI | Tagged , , | 1 Comment

Splitting off LtiLibrary 1.6 Repository

Soon after releasing LtiLibrary 1.6, I started work on LtiLibrary 2.0 using Visual Studio 2017 which had much better tooling for .NET Core than VS 2015. I decided to also pay off some technical debt that had accrued. The outcome was a new naming library scheme (LtiLibrary.NetCore and LtiLibrary.AspNetCore), a new internal naming scheme (e.g. Lti\v1 instead of Lti1), and more consistent controller APIs. All the while, I kept the 1.6 code, solution, and project files in the same repository.

Recently Microsoft release .NET Core 2.0 and an update to VS 2017 with even better .NET Core tooling. Prior to updating LtiLibrary.NetCore and LtiLibrary.AspNetCore to .NET Core 2.0, I decided to split the two code bases. The LtiLibrary 1.6 codebase is now in its own repository. I don’t plan on actively maintaining this repository.

Posted in Uncategorized | 1 Comment

IMS LTI Outcomes 1.0 Versus 2.0

IMS LTI Outcomes 1.0

First introduced in March 2012 with LTI 1.1, Outcomes-1 has changed little over the years. It finally got its own spec in January 2015, and it’s own name: Learning Tools Interoperability Outcomes Management Service 1.0.

In a nutshell, the Tool Provider can read, write, and delete a decimal score between 0.0 and 1.0 as the result, or outcome, of an LTI launch. The Tool Provider can do this immediately, for example if the tool is a self-scoring game. Or later, for example if the teacher launches the tool later to manually grade an essay submitted in response to the original launch.

IMS LTI Outcomes 2.0

A draft version of Outcomes-2 was introduced in December 2014, but it has not been finalized yet. Outcomes-2 has a similarly long name: Learning Tools Interoperability Outcomes Management Service 2.0.

There are many differences between the specifications (see the table below), but as a product manager, I want to focus on the problems they solve. In this sense, Outcomes-2 is a superset of Outcomes-1: every problem you can solve with Outcomes-1 can also be solved by Outcomes-2. But Outcomes-2 can solve one pervasive problem much better than Outcomes-1.

Comparing Outcomes-1 to Outcomes-2

Outcomes-1 Outcomes-2
LTI compatibility LTI 1.x LTI 1.x and 2.x
Service endpoints One Two
Service methods POST
(data content determines action)
DELETE, GET, POST, and PUT
(method determines action in a RESTful way)
Data format XML JSON-LD
Data schemas A single result A single resultAll the results for a single assignment

A single assignment

All the assignments for a class

Result schema Score earned by the learner between 0.0 and 1.0 Score earned by the learner between 0.0 and 1.0The learner that earned the score

Penalty score to be deducted

Extra credit score to be added

Score constraints to be applied

Total score on the assignment

Resulting text score to be displayed

The result status

A comment associated with this result

Assignment schema Not applicable Gradebook labelThe Tool Provider’s activity that produces the associated results

Score constraints to be applied to all associated results

The associated results

What Problems do Outcomes-1 and Outcomes-2 Solve?

Not Enough Time in the Day

Both Outcomes-1 and Outcomes-2 help solve the most common problem teachers want solved: enough time to adjust their teaching to improve student learning. But manually gathering the information they need to adjust their teaching takes a lot of time…time that they are not teaching. Anything that frees up some of their time is a terrific boon. Tool Providers that automatically send information to the teacher are helping that teacher teach and his or her students learn. Even a simple score tells the teacher which students have completed an assignment and if they were able to complete it successfully.

But self-scoring activities and simple scores are not the right fit for all teaching and learning experiences, and that’s were Outcomes-2 starts to shine.

Demonstrating Competence

Teachers often use a style of teaching that asks students to demonstrate their understanding of a concept or their ability to synthesize multiple concepts. There are many ways of demonstrating competence, many of which don’t involve technology at all (such as giving a speech or performing a play). But technology can improve some existing techniques (turning in a document online versus a physically turning in a piece of paper removes the constraints of time and place), and make other techniques possible, such as producing an interactive timeline to demonstrate cause and effect relationships.

One characteristic shared by all demonstrations of competence is the need for an audience, and in particular the teacher. Someone to demonstrate to, and someone to get feedback from. And this is where Outcomes-2 has the advantage.

An Outcomes-2 result has two properties that enable online demonstrations of competence without wasting the teacher’s time and without the constraining either the student or teacher to time and place: the result status and comment. The result status allows the student to tell the teacher where they are in the process: “I’ve started working on assignment” and later, “I’ve completed the assignment”. The result comment can be used to provide more details such as “I’ve finished part 1 of 3”.

Once the result status is “Completed”, the teacher takes over to review the demonstration of competence. Once that review is complete, the teacher can tell the student the final grade and provide simple feedback with the comment (the Tool Provider should provide a mechanism for the teacher to provide detailed feedback if necessary).

Recommendation

Outcomes-2 won’t do anything if it is not built into the learning systems, tools, and content that schools use. Like LTI 2.x, it looks like we are in a stand-off until schools demand that vendors support Outcomes-2. If you are a school trying to implement any kind of online competency based learning, talk to your vendors about how their solution could improve if they implemented Outcomes-2. If you are a vendor with a cool tool that uses Outcomes-2, talk to your schools about how much better your tools would be if their other vendors also supported Outcomes-2.

Posted in Uncategorized | Tagged , , | 5 Comments

LtiLibrary 1.6

So finally, huh!

LtiLibrary (my .NET library for LTI web applications) is now at version 1.6. The big change is the addition Outcomes 2.0 (Draft). Outcomes 2.0 has been in draft status since December 2014. I hope by including support in LtiLibrary, a few people will take up the charge to implement Outcomes 2.0 in production which will help move the spec into final status.

There are two packages on NuGet

You’ll want both if you want Outcomes 2.0. All the source code is on GitHub

Getting Started with Outcomes-2

There are two ApiControllers in LtiLibrary.AspNet that implement the LineItems and LISResult endpoints.

  • LtiLibrary.AspNet.Outcomes.v2.LineItemsControllerBase
  • LtiLibrary.AspNet.Outcomes.v2.ResultsControllerBase

You will need to create your own controllers in your application that inherit from these to handle the actual data. For example,

image

There are two sample applications in LtiSamples that implement Outcomes-2. SimpleLti is a single page app that hosts both the Tool Consumer and Tool Provider. ConsumerCertification is a simple Tool Consumer that makes all the necessary launch requests to a Tool Provider (yours or someone else’s) to exercise the certification tests.

The biggest advantage I see in Outcomes-2 over Outcomes-1 is that Outcomes-2 supports a comment and a status with each result. In K12, publishers are starting to deliver content catalogs via Thin Common Cartridge where each LTI link points to a single activity such as a lesson or quiz. Let’s say those activities require manual grading. With Outcomes-2, the activity can send a “Completed” result (perhaps with a comment like “Submitted”) to tell the teacher the activity is ready for grading. When the teacher grades the submission, the activity could send a “Final” result (with “Good job!”).

Have fun!

Posted in Uncategorized | Tagged , , , | 8 Comments

LtiLibrary 1.5

LtiLibrary (my .NET library for LTI applications) is now at version 1.5. This coincides with IMS releasing the LTI v1.2 and LTI Outcomes v1.0 Final Specifications, http://www.imsglobal.org/lti/.

LtiLibrary 1.5 also supports Content-Item Message v1.0 Public Draft (3 February 2015) and parts of LTI Outcomes Management v2.0 Public Draft (15 December 2015).

LtiLibrary now comes in two parts: LtiLibrary.Core and LtiLibrary.AspNet. Both are available on NuGet,

The source code and samples are available on GitHub,

And the sample Consumer and Provider applications are running on Azure,

If you have any questions or comments about LtiLibrary, please leave them on GitHub.

Andy

Posted in Uncategorized | Tagged , , , | Leave a comment

Moving to GitHub

Two years ago I wrote that I was trying both CodePlex and GitHub. For awhile I kept both in sync. But that got tiresome so I let GitHub fall out of sync and focussed on CodePlex. That worked really well while it was just me working on LtiLibrary. CodePlex was my source control system and it integrated very well with Visual Studio.

But recently, I have had code suggestions from folks which they had to carefully format in the CodePlex issues forum. It worked, but it must have been very hard to write. Even more recently, some friends at work convinced me to use Git instead of TFS. That would allow contributors to submit pull requests that I could easily review and merge into master.

So first I converted the LtiSamples project in CodePlex into a Git repository. No problem. Then I wanted to split LtiSamples into two related repositories. Not so simple. I think I would have to create a new project in CodePlex with a new project site (i.e. separate Issues, Documentation, etc). What I really wanted was one “project” with two repositories.

Well that is exactly what GitHub does: LtiLibrary, LtiSamples

image

So now I’m using Git and the repositories are on GitHub. Wish me luck.

Posted in Uncategorized | Tagged , , , , , | 2 Comments

Splitting LtiLibrary into LtiLibrary.Core and LtiLibrary.AspNet

While I was writing the OWIN middleware for LTI, I noticed that Microsoft and every OWIN middleware contributor created two libraries: one that had no dependencies on ASP.NET and another that did. So I did the same and created LtiLibrary.Owin.Security.Lti (no dependencies on ASP.NET) and LtiLibrary.AspNet.Identity.Owin (dependent on ASP.NET).

That worked out pretty well…so I decided to do the same thing with LtiLibrary. Soon there will be two new packages NuGet to replace LtiLibrary. LtiLibrary.Core is not dependent on ASP.NET, and LtiLibrary.AspNet is dependent on ASP.NET.

Although the LtiLibrary classes, methods, and properties remain the same, the namespace changes, so I will increment the version to 1.5. I’d love feedback on whether I drew the line appropriately…especially while 1.5 has the “-alpha” or “-beta” version suffix.

Posted in Uncategorized | Tagged , , | Leave a comment