Django Canvas OAuth is a Django app that manages OAuth2 Tokens used to make API calls against a Canvas LMS instance.
The OAuth workflow is managed by this library and a CanvasOAuth2Token model is used to store authenticated tokens for each Canvas domain. Users can have multiple tokens (one per Canvas domain) to support multi-environment setups. Tokens are short-lived, so some logic is introduced at the point of retrieving the the stored token to capture and handle the necessary refresh calls.
Requires python >= 3.6 and Django >= 2.0
pip install git+https://github.com/Harvard-University-iCommons/django-canvas-oauth.git#egg=canvas-oauth- Add "canvas_oauth" to your INSTALLED_APPS setting like this:
INSTALLED_APPS = [
# ...
'canvas_oauth.apps.CanvasOAuthConfig',
]- Include the canvas_oauth URLconf in your project urls.py like this:
path('oauth/', include('canvas_oauth.urls')),- Install middleware to begin the oauth2 dance when a token is not present and to consume any errors encountered by the library.
MIDDLEWARE = [
# ...
'canvas_oauth.middleware.OAuthMiddleware',
]- Run
python manage.py migrateto create the canvas_oauth models. - Use the
get_oauth_tokenmethod fromcanvas_oauth.oauthto obtain a token. This method contains all of the logic to obtain a new token, refresh an expired one, or return an existing one.
from canvas_oauth.oauth import get_oauth_token
access_token = get_oauth_token(request)
# Make request to the Canvas API using above token.Settings should be added to your django settings module (e.g. settings.py).
For simple setups with a single Canvas instance, use these settings:
- CANVAS_OAUTH_CLIENT_ID:
- (required for single-environment) The client id is the integer client id value of your Canvas developer key.
- CANVAS_OAUTH_CLIENT_SECRET:
- (required for single-environment) The client secret is the random string (secret) value of your Canvas developer key.
- CANVAS_OAUTH_CANVAS_DOMAIN:
- (required for single-environment) The domain of your canvas instance (e.g. canvas.instructure.com)
For LTI tools working with multiple Canvas instances, use these settings instead:
- CANVAS_OAUTH_ENVIRONMENTS:
- (required for multi-environment) Dictionary defining multiple Canvas instances with their OAuth credentials and domains. See Multi-Environment Support section for details.
- CANVAS_OAUTH_ENVIRONMENT_RESOLVER:
- (required for multi-environment) Class path for environment resolution strategy. Use
'canvas_oauth.resolvers.LtiBasedResolver'for automatic LTI-based detection.
These settings apply to both single and multi-environment configurations:
- CANVAS_OAUTH_SCOPES:
- (optional) Specify a list of Canvas API scopes that the access token will provide access to. Canvas API scopes may be found beneath their corresponding endpoints in the "resources" documentation pages. If the developer key does not require scopes and no scopes are specified, the access token will have access to all scopes. Defaults to
[]. - CANVAS_OAUTH_TOKEN_EXPIRATION_BUFFER:
- (optional) Specify a
datetime.timedeltathat will force a refresh of the access token before it expires according to theexpires_inparameter included in the access token response. Defaults totimedelta(0). - CANVAS_OAUTH_ERROR_TEMPLATE:
- (optional) Specify a template for rendering errors that occur in the authorization flow. Defaults to
oauth_error.html.
For LTI tools that need to work with multiple Canvas instances (e.g., production and test environments), django-canvas-oauth supports automatic environment detection.
- Configure environments in settings:
# Multi-environment configuration
CANVAS_OAUTH_ENVIRONMENTS = {
'production': {
'client_id': 'your_prod_client_id',
'client_secret': 'your_prod_secret',
'canvas_domain': 'canvas.school.edu',
'name': 'School Canvas',
},
'test': {
'client_id': 'your_test_client_id',
'client_secret': 'your_test_secret',
'canvas_domain': 'canvas.test.school.edu',
'name': 'School Canvas (Test)',
},
}
# Enable domain-based environment detection
CANVAS_OAUTH_ENVIRONMENT_RESOLVER = 'canvas_oauth.resolvers.LtiBasedResolver'- Run migrations:
python manage.py migrate canvas_oauth- Add Canvas custom field:
Add this custom field to your LTI Developer Key configuration:
api_domain=$Canvas.api.domain
Example Django view application code for an LTI tool:
Wherever you are making API requests in your code, use the get_oauth_token method to retrieve a token.
Example:
from canvas_oauth.oauth import get_oauth_token
@login_required
def index(request):
access_token = get_oauth_token(request)
# Make request to the Canvas API using above token.Implementation notes:
- The
get_oauth_tokenassumes thatrequest.useris authenticated. - The
get_oauth_tokenmethod will raise anMissingTokenErrorexception if no token is present (e.g. new user). The exception is handled by the middleware, which then initiates the Oauth2 flow. The user will be returned to the original view once the authorization completes successfully. - The
get_oauth_tokenmethod automatically refreshes expired tokens. By default, the token is not refreshed until it has fully expired. However, you can force the token to refresh earlier by configuring an expiration buffer period (defined as a timedelta by the consuming project).
Best practices:
- Avoid storing the access token in a session to use across views. If you do so, your application will be responsible for handling invalid token errors that may arise when the token expires.
Setup environment:
$ python3 -m venv ~/.virtualenvs/django-canvas-oauth
$ source ~/.virtualenvs/django-canvas-oauth/bin/activate
$ pip install -r requirements-dev.txtTo run tests in your venv:
$ python run_tests.pyOr to run tests against multiple versions of python and django use tox:
$ tox
$ tox -e flake8To update the coverage badge:
$ coverage run --source='.' run_tests.py
$ coverage-badge -f -o coverage.svg