OWIN middleware for LTI

TL;DR

An implementation of  OWIN middleware and ASP.NET Identity for LTI is available on Codeplex.

The Complete Story

Microsoft’s ASP.NET Identity now uses OWIN. Anders Abel has a great series of articles explaining the interaction between OWIN middleware and ASP.NET Identity. In this post, I will dig into my version of OWIN middleware for LTI and how to use it in an ASP.NET MVC application using ASP.NET Identity.

OWIN and One-Legged Authentication

LTI uses one-legged OAuth 1.0 authentication. One-legged refers to fact that the application (i.e. Tool Consumer) is authorized directly by the service provider (i.e. Tool Provider). There is no token exchange or third party authorization service.

All of the OWIN middleware that Microsoft writes (and all of the samples I have found) show three-legged authorizations such as that used by Google and Facebook. My OWIN middleware for LTI implements one-legged authentication.

LtiAuthenticationHander

Let’s look at the AuthenticationHandler first. LtiAuthenticationHandler inherits from the abstract Microsoft.Owin.Security.Infrastructure.AuthenticationHandler and implements AuthenticateCoreAsync() and InvokeAsync(). AuthenticateCoreAsync is never used (it only comes into play with three-legged schemes) and simply returns null.

image

The real work is done inside InvokeAsync() which is invoked on each request. The first step is to quickly determine if the request is authenticated with LTI.

image

If it is, the request is parsed into an LtiRequest object and then passed to the application so the application can look up the OAuth secret.

image

If the LtiRequest signature is valid, then the LtiRequest is passed back to the application again so the application can sign in using an application identity.

image

That’s really it for the OWIN pipeline. At this point, the request is authenticated and the application can provide the appropriate services (such as display the tool).

Next, let’s look at how the OWIN middleware talks to the application layer as shown above.

LtiAuthenticationProvider

All of Microsoft’s OWIN middleware use something called a “Provider” to talk to the application layer. The “Provider” is instantiated when the middleware is created.

image

LtiAuthenticationProvider supports two events: Authenticate which is invoked just before LtiAuthenticationHandler checks the OAuth signature, Authenticated which is invoked just after. The application can hook into those events and perform any application layer actions required such as looking things up in the application database.

image

LtiAuthenticationProvider also supports an “event” called GenerateUserName, but this is really used by another application layer method that signs in the user. More on that later.

Everything shown above is in the OWIN middleware for LTI library called LtiLibrary.Owin.Security.Lti. This library is not tied to ASP.NET can can even be used by console apps.

Now let’s look at what goes on in the application layer, namely an MVC Tool Provider.

Startup.Auth.cs

Startup.Auth.cs is where the MVC app pulls in the appropriate OWIN middleware. When you start a new ASP.NET Web App in Visual Studio, the application will be setup to use Microsoft’s CookieAuthentication OWIN middleware and the main authentication scheme.

image

To add support for LTI authentication, simply add the LtiAuthentication middleware.

image

Notice in this example, the OnAuthenticate event is handled by a lamda expression which only recognizes the OAuth key “12345” and returns “secret”. In a real application, you would look up the secret in a database.

OnAuthenticated and OnGenerateUserName use handlers in the SecurityHandler helper class. This is a helper class I wrote to ease the implementation in a typical MVC app. SecurityHandler.OnAuthenticated looks up the matching local user account for the user in the LTI request and signs them in. If a matching account cannot be found, one is created using the username created by SecurityHandler.OnGenerateUserName.

Peek at a Real-ish App

I’ve updated the sample Provider app to use the new OWIN middleware for LTI. As you might expect, the handlers in Startup.Auth.cs are a bit more complex. OnAuthenticate performs an extra security check on the LTI request, and then looks up the OAuth secret in the database.

image

OnAuthenticated records the LtiRequest in the database, then creates an Outcome if needed to handle future simple outcomes interactions with the Tool Consumer. The IDs of both objects are stored as claims in the application user identity.

image

Summary

Prior to OWIN middleware and Microsoft Identity, it was rather tricky to authenticate LTI requests and connect the external user with a local user. With OWIN middleware, it is easy to integrate with Microsoft Identity and make use of Claims to store LTI information relevant to the application.

Advertisements
Posted in Uncategorized | Tagged , , | 1 Comment

LtiLibrary 1.4 on NuGet

LtiLibrary 1.4.2 is now available on NuGet and there is a sample to go with it. The big changes from version 1.3.3 are support for LTI 1.2 Public Draft and LTI Content-Item Message 1.0 Public Draft. You can get both draft specifications from IMS. LtiLibrary release notes are on CodePlex along with all the source code.

The Consumer and Provider websites have been updated to use LtiLibrary 1.4 (and to use the new Microsoft Identity framework so your old username is probably gone). To play around with the Content-Item Message, try this:

  1. Login to Consumer. Make sure you are a teacher.
  2. Create a Course.
  3. In the Course Details view, open the Assignments section and click on Sample Provider Library to launch a content selector running on Provider.
  4. Decide which tool you want to insert into your course, and click on Assign. This will insert the tool into your course.

You may notice that Content-Item Message 1.0 is designed as a browser-to-browser message. When the Content-Item Message handler sends the selection back to the Tool Consumer, the browser is redirected to the Tool Consumer and the Content-Item Message handler state is lost. This makes it different than the server-to-server messaging of LTI Outcomes and Tool Consumer Profile and I’m not sure how I feel about that.

The use cases imagine involve selecting multiple things and sending them back to the Tool Consumer. The process of selecting multiple things might take several complicated steps (such as a search with several filters applied). But as soon as the Content-Item Message handler sends the first selection back to the Tool Consumer, the search results would be lost. This can be overcome by making the Content-Item Message handler capable of assembling several items before sending them back to the Tool Consumer, but it seems like it would be easier to let the Content-Item Message handler send intermediate results back to the Tool Consumer via server-to-server messages with the final “I’m Done” message browser-to-browser.

What do you think?

Provider is also using the new MVC template based on Bootstrap. Consumer is still using the old template based on jQuery UI. After going through the conversion, I found it takes about the same amount of effort to create new views on either. But I suspect Provider will run on mobile devices with less fiddling.

As I mentioned above, both Provider and Consumer are using the new Microsoft Identify framework. Converting from Simple Membership to Identity was pretty straightforward, though tedious. I was surprised how often I was using identity features that are no longer supported or supported in a different way. The only significant change was in the LtiAuthorizeAttribute.OnAuthorization override which no longer adds the signed in user to the authorization cookie. Instead I simply redirect to the destination page and let the Identify framework do the dirty work. This results in one more browser request, but otherwise is quite transparent. [This is also why the browser back button will generate a “you’ve already used that Nonce” message after an LTI launch. I’ll work on that later.]

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

LTI Talks Back with Content Item Message

Here’s a common use case…a teacher finds something online she wants all her kids to use in the classroom. So she copies the URL and pastes it into her learning system (LMS, Google Docs, whatever). This is so common, it adds up to a lot of work (tens of thousands of teachers pasting hundreds of URLs).

I don’t know if this wass the use case the folks at IMS had in mind when they wrote the new Content Item Message spec, but they could have. The new spec works like this:

  1. A teacher launches a “ContentItemSelection” tool from her learning system.
  2. Using the tool, she finds something she wants all her kids to use in the classroom and clicks on it.
  3. The ContentItemSelection tool sends the URL—and everything needed to LTI Launch the resource—back to their learning system.

That’s it. Done. No copy and paste. No “Create Link” forms to fill in. No strange codes to look up.

When she, or any of her kids, launches the resource; all the relevant LTI information is sent along so resource acts appropriately. For example, it may appear in “teacher mode” when teacher launches the resource, but in “game mode” when students launch it.

I have a simple example running on http://consumer.azurewebsites.net that you are welcome to play with. Here’s a short movie showing how easy it is http://screencast.com/t/HsTdm06V2l6I. Don’t blink or you’ll miss it.

Source code for my sample websites can be found at http://ltisamples.codeplex.com.

Posted in Uncategorized | Tagged , | Leave a comment

Sample LTI Provider 1.3

It took a while, but I finally updated the sample Provider website to version 1.3. The source code is here. Like the sample Consumer, the Provider project is now complete. Previous versions were missing files because I did not want to share my passwords to things like the Twitter API. But that made it hard for people to get started. So…I removed the passwords from the files. Some of the other changes:

  • Replaced SimpleMembership with Microsoft ASP.NET Identity 1.0.
  • Removed 3rd party signin support (so I could include all the project files without sharing my credentials).
  • Replaced most LTI code with LtiLibrary.
  • Converted to bootstrap (previous versions used jQuery UI).

I apologize to everyone that added Consumers or Tools to the sample provider site. Because the conversion to ASP.NET Identity changed so many tables, I gave up trying to migrate and wiped out the database.

Have fun!

Posted in Uncategorized | Tagged , , | Leave a comment

Sample LTI Consumer 1.3

I just updated the sample Consumer website to version 1.3. The source code is here. The biggest change is that the Consumer project is now complete. Previous versions were missing files because I did not want to share my passwords to things like the Twitter API. But that made it hard for people to get started. So…I removed the passwords from the files. Some of the other changes:

Despite all these changes, the website itself looks and acts pretty much like it did before.

Have fun!

Posted in Uncategorized | Tagged | 3 Comments

LtiLibrary is now a NuGet Package

To make it even easier to add IMS LTI support to your learning app, I wrapped LtiLibrary into its own NuGet Package. To add LtiLibrary to your application, run the following command in the Package Manager Console:

PM> Install-Package LtiLibrary

There is also an LtiLibrary.Sample package which adds sample MVC and Web Forms to an empty ASP.NET Web Application.

PM> Install-Package LtiLibrary.Sample

Using the LtiLibrary.Sample Package

Want to poke around a simple sample application? Try this:

  1. In Visual Studio start a New Project.
  2. Select the ASP.NET Web Application template:
    New Project dialog in Visual Student 2013
  3. Select the Empty template and add folders and core references for Web Forms, MVC, and Web API:
    New ASP.NET Web Application dialog in Visual Student 2013
  4. After the project is created, enter the following command in the Package Manager Console to install LtiLibrary.Sample:
    Package Manager Console in Visual Studio 2013
  5. Run the project and click on the Launch link to launch a sample tool.
    LtiLibrary.Sample MVC home page

LtiLibrary.Sample has MVC, Web Forms, and Web API sample code that implements LTI 1.0, 1.1, and draft 1.2 services. Here is a table of contents for the sample files:

  • LTI 1.0 Tool Consumer and Tool Provider
    • MVC
      • Tool Consumer
        • Controllers\ConsumerController.cs
        • Views\Consumer\Launch.cshtml
      • Tool Provider
        • Controllers\ProviderController.cs
        • Views\Provider\Tool.cshtml
    • Web Forms
      • Tool Consumer
        • WebForms\Launch.ashx
      • Tool Provider
        • WebForms\Tool.aspx
  • LTI 1.1 Outcomes Service
    • Web API and MVC
      • Tool Consumer
        • Controllers\OutcomesApiController.cs
      • Tool Provider
        • Views\Provider\Outcomes.cshtml
  • LTI 1.2 Tool Consumer Profile
    • Web API
      • Tool Consumer
        • Controllers\ToolConsumerProfileApiController.cs

If you are looking for a more robust example of a Tool Consumer and Tool Provider, check out the LtiSamples code in CodePlex.

Let me know if you find this useful.

Posted in Uncategorized | Tagged , , , , , | 7 Comments

Quickly add LTI consumer outcomes support to your ASP.NET MVC web site

The IMS LTI™ outcomes service allows consumers (such as Learning Management Systems) and providers (such as an online test) to send score information back and forth. Teachers love this! No more manually entering grades into the gradebook.

In this post, I show how to use my .NET LtiLibrary to add consumer outcomes support. My previous post, Quickly add LTI consumer functionality to your ASP.NET MVC web site, describes how to add the LtiLibrary project to your solution.

Add WebApi support to your web site

Use the Package Manager to add WebApi support to your web site project,

image

Then add a WebApiConfig class to the App_Start folder. If you already have one, be sure to enable the XmlSerializer (LTI outcomes use XML formatting),

using System.Web.Http;

namespace WebApplication1
{
   public static class WebApiConfig
   {
      public static void Register(HttpConfiguration config)
      {
         // Use XmlSerializer instead of DataContractSerializer
         config.Formatters.XmlFormatter.UseXmlSerializer = true;

         config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
            );
      }
   }
}

And finally, be sure to call WebApiConfig.Register when the web site starts,

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace WebApplication1
{
   public class MvcApplication : System.Web.HttpApplication
   {
      protected void Application_Start()
      {
         // This needs to appear before the non-api routes
         WebApiConfig.Register(GlobalConfiguration.Configuration);

         AreaRegistration.RegisterAllAreas();

         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
         RouteConfig.RegisterRoutes(RouteTable.Routes);
         BundleConfig.RegisterBundles(BundleTable.Bundles);
      }
   }
}

Implement an OutcomesController

LtiLibrary contains an abstract OutcomesControllerBase with 4 methods that you need to override:

  • GetConsumerSecrets – Return the list of secrets for the given OAuth consumer key. These will be used to validate the outcomes service call from the provider.
  • DeleteResult – Delete the specified score.
  • ReadResult – Read the specified score.
  • ReplaceResult – Save the specific score.

Here is a simple example that works with the IMS LTI™ test tool,

using System.Collections.Generic;

namespace WebApplication1.Controllers
{
   public class OutcomesController : LtiLibrary.Consumer.OutcomesControllerBase
   {
      private static double? Score;

      protected override bool DeleteResult(LtiLibrary.Consumer.Result result)
      {
         Score = null;
         return true;
      }

      protected override IList<string> GetConsumerSecrets(string consumerKey)
      {
         return consumerKey.Equals("12345") ? new List<string> { "secret" } : new List<string>();
      }

      protected override bool ReadResult(LtiLibrary.Consumer.Result result)
      {
         result.DoubleValue = Score;
         return true;
      }

      protected override bool ReplaceResult(LtiLibrary.Consumer.Result result)
      {
         Score = result.DoubleValue;
         return true;
      }
   }
}

Tell the provider that you support outcomes

The last step is to tell the provider that you support outcomes. You do this by adding two values to the LTI request: the endpoint URL of the outcomes service, and the result ID which you can think of as the grade cell ID in the grade book. For example,

public ActionResult Launch()
{
   var request = new LtiLibrary.Consumer.LtiOutboundRequest
      {
         ConsumerKey = "12345",
         ConsumerSecret = "secret",
         ContextId = "1",
         ResourceLinkId = "1",
         Url = "http://www.imsglobal.org/developers/LTI/test/v1p1/tool.php"
      };

   // Add outcomes support
   var urlHelper = new UrlHelper(this.HttpContext.Request.RequestContext);
   var relativeUrl = urlHelper.HttpRouteUrl("DefaultApi", new { controller = "outcomes" });
   request.LisOutcomeServiceUrl = new Uri(this.HttpContext.Request.Url, relativeUrl).AbsoluteUri;
   request.LisResultSourcedId = "1";

   return View(request.GetLtiRequestModel());
}

Launch

If you launch the IMS LTI™ test tool, you will see a new option to test outcomes,

image

What’s Next?

Check out the sample Consumer in LTI Samples to see a more robust use of LtiLibrary. If you are a publisher, the sample Provider shows how to use LtiLibrary to handle LTI launch requests and even send grades back to the consumer.

Posted in Uncategorized | Tagged , , | 4 Comments