.NET - OAuth2 Workflow: Part 1 - Authrorize Redirect

garth
Community Champion
8
9117

Recently I was asked to create a custom grading tool based on rubrics, which requires modifying student grade data.  I had created a LTI toolkit to run some reports and automate SIS integration workflows, in that scenario I was using a token generated in a service account.  But using a service account token to modify grades is not an accepted method.  For grading we want to be able to verify a few important details and take advantage of existing functionality within Canvas:

  • The user has access to the course (masquerading is technically possible, but prone to errors)
  • The user has privileges to modify grades
  • Leverage the existing "View Grading History" feature build into Canvas

Canvas documentation on the OAuth2 workflow can be found here: OAuth2 Overview documentation

The example used for this post assumes the application will be launched from within Canvas using LTI integration.  If you have not yet worked through OAuth and LTI, these articles can provide an overview of how to get started before you dive into OAuth2:

Part 2 and 3 of this example can be found here:

Before you get started...

To get through this example, you will need to have the following knowledge and tools

  • You have successfully integrated at least one custom application that you have written with Canvas using LTI, which also implies the following:
    • You have a development environment setup and ready to go (language of your choice)
    • Your environment includes a web server
    • You have configured your web server with an SSL certificate
  • Have access to a database to cache information
  • You have administrative access to a test instance of Canvas
  • You are familiar with making API calls
  • You are familiar with using Git code repositories

If you have experience with web apps, this is not complicated.  This walk through should be straight forward.

Source code for this walk through can be found here:

The branch of interest for this walk-through:

Getting Things Ready in Canavs

The first step towards OAuth2 is completing the LTI install of your application within your Canvas instance.  If you have not created an LTI application yet, I suggest completing the three LTI posts mentioned above, I will not cover those steps again here.  .NET LTI Project - Part 1 - Connect to Canvas covers steps for registering your application in Canvas.

For the simple application associated with this example, I generated the LTI XML using the tool found here:

Here is the result (substitute your.domain.com with the URL specific to your test environment):

<?xml version="1.0" encoding="UTF-8"?>
<cartridge_basiclti_link xmlns="http://www.imsglobal.org/xsd/imslticc_v1p0"
xmlns:blti = "http://www.imsglobal.org/xsd/imsbasiclti_v1p0"
xmlns:lticm ="http://www.imsglobal.org/xsd/imslticm_v1p0"
xmlns:lticp ="http://www.imsglobal.org/xsd/imslticp_v1p0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.imsglobal.org/xsd/imslticc_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticc_v1p0.xsd
http://www.imsglobal.org/xsd/imsbasiclti_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imsbasiclti_v1p0.xsd
http://www.imsglobal.org/xsd/imslticm_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticm_v1p0.xsd
http://www.imsglobal.org/xsd/imslticp_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticp_v1p0.xsd">
<blti:title>OAuth2 Test</blti:title>
<blti:description>Test app to prove out Canvas OAuth2 worflow</blti:description>
<blti:icon></blti:icon>
<blti:launch_url>https://your.domain.com/home/oauth2Request</blti:launch_url>
<blti:extensions platform="canvas.instructure.com">
<lticm:property name="tool_id">D48C2D1E-C3BC-443E-9BA3-BDF000FDC91B</lticm:property>
<lticm:property name="privacy_level">public</lticm:property>
<lticm:property name="domain">your.domain.com</lticm:property>
<lticm:options name="course_navigation">
<lticm:property name="url">https://your.domain.com/home/oauth2Request</lticm:property>
<lticm:property name="text">OAuth2 Test</lticm:property>
<lticm:property name="visibility">admins</lticm:property>
<lticm:property name="default">enabled</lticm:property>
<lticm:property name="enabled">true</lticm:property>
</lticm:options>
</blti:extensions>
<cartridge_bundle identifierref="BLTI001_Bundle"/>
<cartridge_icon identifierref="BLTI001_Icon"/>
</cartridge_basiclti_link>

Point of interest in the XML:

The XML configuration above should create a Course menu option labeled "Oauth2 Test".

When you install the application in Canvas, make a note of the consumer key and shared secret for reference later.

You will also need to generate a "Developer Key".  Follow the steps in this Canvas article:

For the purposes of the exercise, use the following redirect URL (with your domain):

  • https://your.domain.com/home/oauth2response
    • This is not the same URL defined in the XML

This method call (/home/oauth2response) will line up with the source code associated with this example.

The result of creating the developer key will generate a unique ID for your redirect:

225693_pastedImage_4.png

Required Variables

There are three <appSettings> variables that we will use for this first run at the OAuth2 workflow.

  1. "oauth2ClientId" - this is the value created in the Developer Key (screen shot above)
  2. "consumerKey" - this is the consuer key you assigned during the LTI install of your application
  3. "consumerSecret" - this is the shared secret you assigned during the LTI install of your application

225694_pastedImage_11.png

Store these three variables in the web.config, see the associated source code for a reference.

Code Walk-thru

Using the sample project as an aide, I'll walk you through the first part of the OAuth2 workflow which is very straight forward.  The sample project was created from the default ASP.NET MVC project template, and will use the default HomeController.

Note:  The sample project was created with as few steps as possible, it is a simple MVC application with a single controller.  This is not a recommended template for a production application, but only an attempt to provide a simple, clean easy to follow example.  After following along with this example, I strongly recommend thinking through the steps, and building your own solution.

Home Controller

Using Visual Studio to view the source code you will find a single controller: HomeController.cs

If you browse to the default URL you will see the home page  (https://your.domain.com)  There is nothing fancy, basically the default home page that was generated by the MVC template with modified content and links.  You can use this page to verify your web server is setup properly.

There are two additional methods associated with this example:

The OAauth2 Request Method: oauth2Request()

This is the method defined in the LTI XML above, the method that Canvas will use to execute the launch request.  The LTI launch request must be a POST.  If you would like to test this method you can use Postman to debug this method manually by making a POST calll to https://your.domain.com/home/oauth2Request

I have included helper classes that will handle LTI parameters received (oauthHelper.cs and ltiLaundhParams.cs), more importantly it will parse the LTI launch parameters and validate the OAuth signature.  Step through the code if you would like to learn more about LTI launch parameters.  A manual call from Postman will fail validation and return the result.cshtml view with an associated message.

With the debugger running, launch the app from Canvas and confirm that LTI validation is successful.  Validation of the OAuth signature depends on the "consuerKey" and "consumerSecret" that you stored in the web.config.  Looking at the constructor of oauthHelper.cs you will see where the "consuerSecret" is read from the config file and the OAuth signature is verified.  Once the LTI parameters pass validation the response will be a redirect back to Canvas.  In the browser you should see a prompt from Canvas asking the user to authorize access on behalf of the user.


225768_pastedImage_19.png

By clicking "Authorize" you are prompting Canvas to redirect back to the application server, your web server, at the URL you defined when you created the Developer Key earlier:  https://your.domain.com/home/oauth2Response


244405_devkey.png

The OAuth2 Response Method: oauth2Response()

This is the method that Canvas will call if the user clicks the "Authorize" button, shown in the screen shot above.  In the source code associated with this example, the web server will return a simple Success!! message to verify that the loop has been closed.  Get this much of the application working and you have Step 1 working, and part of Step 2.

225789_pastedImage_1.png

Quick Overview

  1. Create a Developer Key, pointing the redirect URL to your "oauth2Response" method
  2. Install your application in Canvas, directing the LTI launch URL to the "oauth2Request" method.
  3. Make sure your web.config has the three required parameters:
    • oauth2ClientId
    • consumerKey
    • consumerSecret
  4. Launch the app from Canvas, confirm that the OAuth signature passes validation and you receive the "Authorize" option in the browser
  5. Click "Authorize" and confirm you receive the "Sucess!!" response in the browser

Summary

I felt this was a good place to start, enough to get things working without being too overwhelming.  Getting this far you have verified that your dev environment is working properly, and that Canvas is properly configured.  Next steps include requesting a user token and caching some values for reuse.  If you don't have a database available there are many options:

I have Sql Server in my dev environment and will be using that for examples, but any database will do.

Part 2 will be posted as soon as I can make the time to get it down on "paper".

I am happy to try and answer any questions that come up, and happy to edit this post based on feedback.

Hopefully this will be useful to someone out there.

Part 2 and 3 of this example can be found here:

8 Comments
mmitchell
Community Contributor

Garth,

These walk throughs are gold. I've been trying to build an LTI for over a year now and, thanks to your info, I've finally made progress.

Thank you for posting!

garth
Community Champion

 @mmitchell ‌ it's really nice to get the feedback, thank you : )

s528180
Community Novice

Can we perform something on click of Cancel button in the authorization window? I wanted to know which request it performs?

Thanks

garth
Community Champion

During OAuth authorization if the user clicks "Cancel" they are presented with a simple message:

  • {"message":"An error has occurred."}

The authorization page is running on the Canvas server, Canvas is asking the user if they are authorizing your app.  In other words, when the user clicks "Cancel", Canvas does not redirect back to your server.

I don't think you have an opportunity to respond to "Cancel", it is handled by Canvas.

If I'm not understanding, please correct me Smiley Happy

s528180
Community Novice

I wanted to load my own page in that case. Can we implement it server side?

garth
Community Champion

Ha, I just re-read this document:  OAuth2 - Canvas LMS REST API Documentation 

To quote:

"If the user doesn't accept the request for access, or if another error occurs, Canvas redirects back to your request_uri with an error parameter, rather than a code parameter, in the query string."

It has been a while since I've been working in this piece of code, but if you look at the sample code here:

  • public async Task<ActionResult> oauth2Response(string code = null, string state = null, string error = null)

You will see if...else logic to handle the error string if one is returned..

Sorry for my previous post, I should have actually look more closely, my apologies.

If you put a break point on the first if statement, then click "Cancel" at the authorize screen, you should get an error string that says "access_denied"  (see screenshot):

240523_pastedImage_6.png

Again very sorry for my last reply, moving too fast today.

s528180
Community Novice

Thanks, that solves my issue.

BTW I have implemented the OAUTH2 in node.js

garth
Community Champion

Awesome!  You should write it up in a blog to share with the community Smiley Happy