Javascript Themes
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi everyone,
I'm working on a tool that adds a custom button to the Canvas interface using JavaScript injected via a Canvas Theme. The goal is to allow teachers to calculate and update rubric scores for an assignment (e.g., entering an average mastery score into a rubric criterion using the Canvas API).
So far, I'm able to:
Detect the correct page (e.g., Gradebook)
Inject the custom UI
Calculate mastery averages using GET requests (e.g., /outcome_rollups)
However, when I try to submit a PUT request to update grades using the rubric_assessment parameter (e.g., to /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id), the request fails with a 422 error. I assume this is because the Theme script is not authenticated with a valid access token.
My questions:
Is it possible for a Theme-injected script to gain write access to Canvas APIs (e.g., grading endpoints)?
If not, is the recommended approach to offload write access to a backend service or Chrome Extension that has access to a secure token?
I want to make sure I'm not overlooking an officially supported method before I shift my architecture to use a server or browser extension.
Thanks in advance for your insights!
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi @Julia_C,
Thanks for the additional details! It's been so long since I did anything with a put/post as part of theme javascript that I forgot authorization is needed there. Instead of using the api token method, you can get the csrfToken cookie and use that value for auth in a slightly different way. Try the following to see if it works (I just put this together from your code and some snippets out of one of my old projects):
function getCookie(name) {
const cookies = document.cookie.split(';').map(cookie => cookie.trim());
let cookieValue = null;
let i = 0;
while (i < cookies.length && cookieValue === null) {
const cookie = cookies[i].split('=', 2);
if (cookie[0] === name) {
cookieValue = decodeURIComponent(cookie[1]);
}
i++;
}
return cookieValue;
}
async function submitRubricScore(courseId, assignmentId, userId, criterionId, score) {
const csrfToken = getCookie('_csrf_token');
const payload = {
authenticity_token: csrfToken,
rubric_assessment: {
[criterionId.toString()]: {
points: score,
comments: "Auto-calculated average mastery score"
}
}
};
console.log("Submitting rubric score for user", userId, payload);
const response = await fetch(`/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`, {
method: "PUT",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
console.error("Submission failed for user", userId, "→", errorText);
throw new Error(`Failed to update user ${userId}: ${errorText}`);
}
console.log("Score submitted successfully for user", userId);
}
Let us know if this runs successfully or not!
-Chris