.NET LTI Project - Part 3 - OAuth

garth
Community Champion
7
9552

In .NET LTI Project - Part 2 - Launch Request we looked at the variables that are received in the launch request.  These variables are a vital part of user authentication using OAuth.

In very general terms, OAuth allows an authenticated user in Canvas to be identified and authenticated in your application.  Canvas will send you the identity of the user in the launch request, and you will use OAuth to verify that the identity sent to you has not be tampered with.  Once you have verified the integrity of the credentials you have received, you can masquerade as that user.

Consumer Key and Shared Secret

In the first article .NET LTI Project - Part 1 - Connect to Canvas​ we looked at how to connect your application to Canvas using the XML configuration.  Part of that process included the Consumer Key and Consumer Secret.  These two values now come into play.

In the launch request, Canvas will send you the Consumer Key which is a unique identifier.  Think of it as a user id.  If you are working with multiple institutions, or multiple applications, this would be the "user id" for each institution or application.  For example, if you are creating multiple LTI tools for your institution, this would be the unique id for each tool that you are exposing.  Or if you are supplying a tool to multiple institutions, this would be the unique id for each institution.

The Shared Secret is the password.  Canvas will never send you the Shared Secret, it is meant to be private, and should only be known by you and Canvas.  Canvas will use the Shared Secret to create an encrypted hash of the form parameters that are sent to you in the launch request as the oauth_signature.

When you receive the launch request, you will look at the variable oauth_consumer_key to lookup the shared secret on your end.  You will then use the Shared Secret to validate the OAuth signature found in the launch request parameter oauth_signature.

Make sure you understand the importance of the values Consumer Key and Shared Secret, and think about how you are going to store and retrieve them in your app.  What naming convention will you use to generate your Consumer Key and Shared Secret?  Would something like a GUID make sense, at least for the Shared Secret?

More Detailed OAuth Information

Here is a site that talks about the OAuth variables, and the sequence of operations in general.  This will give you some good information if you want to dig in and understand all of the steps involved:

As I wrote my own OAuth class, I ran into a wall and got stuck.  As I tried to figure out what I was doing wrong, I found this library:   DotNetOpenAuth

Note:  I did have to make an update to the code to properly handl URL encoding.  If you use this library, take the time to debug and understand the steps.

Having an example to illustrate is sometimes very helpful to get you over a hump, and this code helped me to find where I was missing an iteration of URL encoding.  If nothing else, working through this code might help to understand the steps involved, and see them in action.

If you decide to write your own OAuth algorithm, I suggest referencing this tutorial to validate your results:

If you can reproduce results from a known set of variables, you are well on your way.  This sandbox also discusses the basic requirements to generate the OAuth signature.

NOTE: Make sure you understand the security structure, and the role of the nonce and timestamp, and have a strategy to verify unique messages.  This is definitely not a "security how-to".

Why Fight It?  Find a library.

In the end I decided to use a library.  If someone has already taken the time to create and debug a well established technology, could it be more stable than my own home grown version?  I eventually found that IMS Global (the authority on LTI) has published sample code.  At that point I put my own library to bed, and adopted the IMS code.  They have sample code for multiple languages here:

Source code for this library can be found here:

Using this library, validating the OAuth signature is one line of code.  For ease of readability, and to call out the namespace where you can find the method of interest, this is a bit long winded:

bool signatureVerified = (LtiLibrary.Core.OAuth.OAuthUtility.GenerateSignature(request.HttpMethod, request.Url, request.Form, consumerSecret) == request.Form["oauth_signature"]);

When you receive the launch request, that's when you want to validate the OAuth signature.

Calling GenerateSignature and passing in the variables will return a string.  If that string does not match the string you received from Canvas in the oauth_signature variable, reject the request and do not allow the user to access your app.

In relation to the test project used in .NET LTI Project - Part 1 - Connect to Canvas you would use the line of code in your HelloWorld controller in the following methods:

  • Index() - which we configured to receive a launch request from the Account navigation menu
  • Welcome() - which we configured to receive a launch request from the Course navigation menu

You will need to validate the signature in any method where you are receiving a launch request from Canvas.

NOTE: Keep in mind, once Canvas has launched your app you will not recieve any more data from Canvas, your application runs on your server.  Something to think about and I'll cover some details to think about as we continue on.

It is also important to note that I am passing in the Consumer Secret.  This is the private key that is shared between you and Canvas, and is used to generate the hashed signature string.  You need to be able to look this value up based on where the request is coming from.  Again, if you have multiple institutions accessing your tool I strongly recommend having a unique Consumer Secret for each institution.

Perhaps the best reason to use a trusted library is that you are able to focus on your business logic, you are able to focus on the functions of your app, and not spend time rewriting well established logic.

Conclusion

So, validating the signature can be as simple as a single line of code.  Hurray!

But, you will learn a lot by writing your own if you have the time.

It should be easy to test your validation now:

  • Enter a different Consumer Key and Secret in Canvas vs. your app and make sure validation fails
  • Try sending the same nonce, how are you handling that?
  • Try sending an earlier timestamp, how are you handling that?
  • Try modifying other values sent in the Form params, does your signature still validate?
  • What does the user see if their request is rejected?

It is really up to you to flush out your test cases.

Getting past OAuth has been a topic of discussion in several places, I hope this helps.

There are several considerations once you get past authentication, I'll mention some of them in another post.

A basic Visual Studio project has been published here: LTI Demo Project​

7 Comments
george_markaria
Community Explorer

Hi  @garth ​,

Where can I download the demo source code of the ltiDemo application you are referring to in these posts?

I'm currently developing an application in .NET which will connect to the API and this may be helpful.

Thanks.

garth
Community Champion

 @george_markaria ​ start with the Part 1:

Part 1 uses a standard ASP.NET MVC project from an online tutorial, see the section "Common Starting Point".

My intention in part one was to show how to connect your app to Canvas, regardless of what it does.  In other words, starting with a generic plain-jane app, you can link your app in Canvas very easily.

Part 2 builds on that sample app:

The primary focus of Part 2 is to explore the launch request, and the LTI variables that you should expect to receive from Canvas, and make sure people know where to look for those variables.

Part 3 focuses on OAuth and user authentication.  After that, it is really up to you to decide what your app is going to do.

I am considering a Part 4 to discuss some things to think about, and hope to get to it as time permits. 

If I were to post my ltiDemo app, it would be nothing but the tutorial code from this article linked in Part 1:

One modification would be that I installed the IMS Global Nuget package.

If you work through Part 1, 2, and 3 you will create the same project that I have.

However, I will get the project posted where it can be downloaded, and will update a link when it's ready.

garth
Community Champion

 @george_markaria ​ I have posted the .NET project on bitbucket:

This is demo code only, and goes no further than what is discussed in these posts.

There is a sample XML file to submit to Canvas, you will need to change the domain name to match your environment.

garth
Community Champion

If you found this post helpful, I am working a walk-through of the OAuth2 workflow: 

The first part is published here, and includes launching an LTI app:

mwwhited
Community Member

FYI all of the cross links are broken on this blog post.

James
Community Champion

@mwwhited 

The blog post was written in 2016. In 2020, Canvas switched software that they used for the forums and every link changed. They brought all of the old posts over so no knowledge was lost, but did not relink or correct all of the URLs.

For the time being, searching for titles is your best hope of finding things.

DavidHenry
Community Member

@garth I know it's 5 years later, but I wanted to say your code still works great!  I incorporated it into an MVC .Net Core application with a web UI front end.  I tested it with getCourtseDetails and it works. I hope to be able to add communication channels commands.  Everything is so well organized and self explanatory in the code.  It was intimidating at first, but it all eventually made sense to me.  I really appreciate the time and work you put into this project.  Thank you.