Comments (60)
@Cvmcosta Yes man, that will be great if you have an endpoint to provide the keys. Thanks man and great work
from ltijs.
@Cvmcosta yes, I can launch it, it works as I would expect. I even tried to open it in a new window to avoid any troubles with an iframe. Here is the debug output:
~/ltijs-examples/lti-provider$ DEBUG='provider:*' nodejs index.js
provider:main Attempting to connect to database +0ms
(node:239322) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
provider:database Database connected +0ms
provider:database Database connection open +1ms
_ _______ _____ _ _____
| | |__ __|_ _| | |/ ____|
| | | | | | | | (___
| | | | | | _ | |\___ \
| |____| | _| |_ | |__| |____) |
|______|_| |_____(_)____/|_____/
LTI Provider is listening on port 56789!
LTI provider config:
>Initiate login URL: /14eed217-2d3c-4975-a381-b69edcb40e0e/port/56789/login
>App Url: /14eed217-2d3c-4975-a381-b69edcb40e0e/port/56789/start
>Session Timeout Url: /14eed217-2d3c-4975-a381-b69edcb40e0e/port/56789/sessionTimeout
>Invalid Token Url: /14eed217-2d3c-4975-a381-b69edcb40e0e/port/56789/invalidToken
-----BEGIN PUBLIC KEY-----
[...]
-----END PUBLIC KEY-----
Deployed!
provider:main Receiving a login request from: https://moodletest.DOMAIN.com +4s
provider:main Redirecting to platform authentication endpoint +2ms
provider:main No cookie found +2s
provider:main Received request containing token. Sending for validation +0ms
provider:auth Attempting to retrieve registered platform +0ms
provider:auth Retrieving key from jwk_set +3ms
provider:auth Attempting to verify JWT with the given key +185ms
provider:auth Token signature verified +1ms
provider:auth Initiating OIDC aditional validation steps +0ms
provider:auth Validating if aud (Audience) claim matches the value of the tool's clientId given by the platform +0ms
provider:auth Aud claim: OaHoDdIprwVpaAa +0ms
provider:auth Checking alg claim. Alg: RS256 +0ms
provider:auth Checking iat claim to prevent old tokens from being passed. +0ms
provider:auth Iat claim: 1577793248 +1ms
provider:auth Current_time: 1577793250.073 +0ms
provider:auth Time passed: 2.072999954223633 +0ms
provider:auth Validating nonce +0ms
provider:auth Nonce: MGD0zs5+OlLaaDZvanzh2g== +0ms
provider:auth Tool's clientId: OaHoDdIprwVpaAa +0ms
provider:auth Storing nonce +2ms
provider:auth Successfully validated token! +0ms
provider:main Passing request to next handler +213ms
provider:main Setting up path cookie for this resource with path: /14eed217-2d3c-4975-a381-b69edcb40e0e/port/56789/main +2ms
provider:auth Cookie found +425ms
provider:main Passing request to next handler +417ms
provider:auth Cookie found +2s
provider:main Passing request to next handler +2s
provider:gradeService Target platform: https://moodletest.DOMAIN.com +0ms
provider:gradeService Attempting to retrieve platform access_token for [https://moodletest.DOMAIN.com] +2ms
provider:platform Access_token found +0ms
provider:gradeService Access_token retrieved for [https://moodletest.DOMAIN.com] +2ms
provider:gradeService Response code 401 (Unauthorized) +200ms
I copied the public key and here is a screenshot of these extra settings in moodle
from ltijs.
@Cvmcosta Hi again just to keep the forum updated, i will be testing out your recommendations over the coming days. Once i have an update i will post back to the forum. Thanks again for the help.
from ltijs.
Aaaah, i think i get it. I don't see how you would be able to do this without altering Ltijs.
You would have to change how Platforms are registered (to use Auth0 instead) and then change how these keys and Platforms are retrieved to get this data from Auth0.
But have in mind that Ltijs already provides all of this functionality, i don't see a reason to use Auth0. As this person said, LTI doesnt have sign-in, it only uses the Client Credentials grant, and all of this functionality is already implemented.
from ltijs.
@Cvmcosta Hi sorry as well its been a while. I think the source of my issues was that i was trying to use a production instance of canvas with https with my localhost that was running http. Once i put up the test server on Heroku i did get the two sides communicating. Unfortunately i didn't finish working on this as i didn't have the support of my employer to continue. I can say that i was satisfied this would work but obviously to make sure that both the server and canvas are hosted via https
from ltijs.
Hello, thanks for posting this issue. I've actually had a hard time getting lti 1.3 to work with canvas, last time i tried their implementation didn't seem complete. I'll take some time tomorrow morning to look into this issue and see if i can fix it or at least find out whats going wrong.
from ltijs.
Hello @gentlemanjohn , sorry it took me this long to follow up on this, i've been very busy.
In the pas couple of days i've been trying to get ltijs to work with canvas, with no success.
I think i might be doing something wrong, either that or the lti functionalities within canvas don't work properly on self hosted instances (Judging from a few weird behaviors i noticed while working on it).
I requested a Canvas live demo to have a better way of testing this.
In the meantime, can you tell me how are you configuring your LTIJS?
And you are using a instructure hosted canvas instance, right?
from ltijs.
@Cvmcosta no problem, I've been pulled away from this the last few weeks as well.
Yes, our instance is hosted by Instructure. Here is my LTI provider config:
const lti = new LTI(
'<Canvas Developer Key Secret>',
{
url: 'mongodb://localhost/database'
},
{
appUrl: appBaseURL + 'launch',
loginUrl: appBaseURL + 'login',
invalidTokenUrl: appBaseURL + 'invalid-token',
sessionTimeoutUrl: appBaseURL + 'sessionTimeoutUrl',
logger: true
}
);
Platform:
let plat = await lti.registerPlatform({
url: 'https://canvas.test.instructure.com',
name: 'Canvas',
clientId: '<Canvas Developer Key Client Id>',
authenticationEndpoint: canvasTestBaseURL + '/api/lti/authorize_redirect',
accesstokenEndpoint: universityCanvasTestBaseURL + '/login/oauth2/token',
authConfig: {
method: 'JWK_SET',
key: canvasTestBaseURL + '/api/lti/security/jwks'
}
});
from ltijs.
@gentlemanjohn Hello, can you tell me what public key you are using in the canvas External tool registration? If i understand correctly, Canvas accepts either a JWK Set endpoint (which LTIJS does not support yet), or a JWK key. Did you convert the RSA public key generated to JWK?
I'm going on a process of elimination here. If the problem is not the JWT sent, i'll check the lti protocol to see if there's something missing.
from ltijs.
Hey @Cvmcosta I had to set up my Canvas Developer Key again (our test instance of Canvas is reset every few weeks) and now I'm having a hard time reproducing the error. I'm getting directed to invalidToken when I try to connect. I thought I had set everything up in Canvas and LTIjs the same way, but obviously I'm doing something else wrong now.
To your question:
I'm not sure what you mean by convert the RSA public key generated to JWK
. I'm just using a sample JWK that I found.
In the Canvas Developer Key setup, the required "JWK Method" field will accept "Public JWK" (with corresponding text box to paste a JWK) or "Public JWK URL." I selected "Public JWK" and pasted in the following:
{
"e": "AQAB",
"n": "a19db7eb5bd2999124c1b6f6b062ef5805af1fce4de9cd86149d890bd44dfc9d1c6b9beb666450693c5db10e9714c0ccbf87d3c466c8eb6f3d68d731191f5e0303162569bedbf6bd8fcea6fb7b0f81f3e64d51afcdb71290dac9f2ad688a6425184be46bb12e70836507255cff76b5f640032c99843f334f7d059a31a0d0ce7e9d2f36c80742709fc82b929e2def3c5abddccf1fde49867271060849f5784ade538dd30f51544ccc3bee17510fa8e7c2b523de7c457c4e3327f1461939c4f651ecb2630128aa2af72676747da372b6e2a357344201c3028645c16135d85b3254628e70621ce32b9d3bbf94e4d66a209bb3a55bd23eac00e09a01a60dfbc3130e7783510ef64849c95ce4d855f8687f19dc74364dfc3f2027184963bde0081d8e34f365c58e8b205a1ab4776c49f098171c2877e8328bb3a5554565960afa701834172997ded835aac39501133780fc8278d21c38a996194f6572ffbc251722ea73bebd6426317a185c2540894f1adc5b00db87f650292e3765460f542e33ca503d1977ca55b0062ee1fa62cc93e256b6eed47f219f57984a4d4921072edeac99d875631dd2a4b0d84caae0e5fa1ab6b19fbdf2d6355a3c0dbdfa82ea87733e022bd2d2dec4c3e51451b1f8fa47a4188cabfdc65bb39149650e72ab204cbbe1e840e31133e1cd8e2148a1f2445785e6bf982dfe348a9a2aca5ef6db5a84c301762d43dd1933b875ca4202fd8afee0afb9dd685599c985b50ed0d7c1ed6c0b6f92c01bf8eb8f5ccbdc64234a0d1cbc2b7a1e4a964058d7c4f4e413b8297c3a7a51569386e211c090b1230cb8f90e622804e1f74816c6fab9d71e1c5e7764c5ced695af63afd8bfedd0262d22e92a617b4f9a2dd2bd5ef7cb1320210bdead16286f886107fe76caf3528a024525e3ea69825a2886f68def8eeb706857ffe6aa52b3a005473a08763af5b29ceb14b7caad605657c5a59a7195f36a655c2d440d477015735e1366ca2497b132846a66749792243d4f3f236eb8cd2518abc87a24ae42d1d5fb090759506eb0c460a908bc3f6237557c5769473c556b699b7b4551501c748a4f1e609ca5948b98d1b23776c08001a47e442b8e55c00234c8a8bdf4f6d3d1a1458331b078504f10df3eb4603013c7ab69d5c4327fe9ff00b39dc882221c",
"alg": "RS256",
"kid": "726058f26aa6fb8b7ae36755161f7a0f",
"kty": "RSA",
"use": "sig"
}
I'll play around some more, but hopefully that gives you some insight...
from ltijs.
Hi @gentlemanjohn , i think i know what is going on. This jwk key canvas requires is used to get the access token, LTIJS uses the corresponding private key to sign the request for the access token and them Canvas (any lms) uses the public key to verify that request and return the token. So now you have two options (assuming you are able to return to the state of the initial error), you can either convert the public RSA key of your platform (canvas) to jwk and pass that to canvas, or wait until i implement the jwk key set, which will take about a week.
You can get the RSA key like this:
lti.getPlatform('https://canvas.test.instructure.com').then(async plat => {
console.log(await plat.platformPublicKey())
})
And then you can convert that rsa key to jwk here.
Use the key in the Canvas registration and let me know if it worked. Im hoping that fixes (Or at least changes) the problem :)
from ltijs.
I've also no luck with Grade.ScorePublish and Moodle. All I get, at best, is Response code 401 (Unauthorized)
I'm also interested in that "Names and Role Provisioning Services" and for that I have to add a scope in the Auth.js file in getAccessToken
? at least, I think that's necessary. In any case, I get the same error. That access token has an additional scope, though:
{ access_token: '[some hex-like string]',
token_type: 'Bearer',
expires_in: 3600,
scope:
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem https://purl.imsglobal.org/spec/lti-ags/scope/score https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly' }
from ltijs.
@haraldschilly Did you configure moodle with the public key generated by ltijs for that specific platform?
from ltijs.
@Cvmcosta yes, I copied over the public key I got via plat.platformPublicKey()
where plat
is lti.getPlatform(iss string)
. I'll try to just register again, maybe something is out of sync.
from ltijs.
@haraldschilly Okay, please let me know how it goes. I'm working on a step by step tutorial for setting up and sending grades.
from ltijs.
@Cvmcosta well, I did reset and reconfigure everything, but I still get the same error. Please let me know if you have a guide ready, I would really appreciate it :-)
from ltijs.
@haraldschilly I wrote a tutorial on medium, here.
I think the issue might be the request to the route you are using to send the grade. It needs to receive the ltik token through the query parameters. So if you are calling '/sendgrade' from the client, you should call '/sendgrade?ltik=LTIKKEY'.
This key is passed to every route you call with lti.redirect, and can be retrieved in the client with:
const searchParams = new URLSearchParams(window.location.search)
var ltik = searchParams.get('ltik')
from ltijs.
Hi @Cvmcosta @haraldschilly @gentlemanjohn ,
I know this issue is not related but could be something similar
I have an issue that lti.onConnect
doesn't get called at all, I am using Canvas self hosted version and here is my code:
import { Provider } from 'ltijs'
const lti = new Provider(
'Secret',
{ plugin: db },
{
appUrl: 'http://lvh.me:3000/lti',
invalidTokenUrl: 'http://lvh.me:3000/invalid-token',
logger: true,
loginUrl: 'http://lvh.me:3000/login',
sessionTimeoutUrl: 'http://lvh.me:3000/sessionTimeoutUrl'
}
)
const ltijsFunction = async () => {
await lti.deploy()
const plat = await lti.registerPlatform({
accesstokenEndpoint: 'http://localhost:4000/login/oauth2/token',
authConfig: {
key: 'http://localhost:4000/api/lti/security/jwks',
method: 'JWK_SET'
},
authenticationEndpoint: 'http://localhost:4000/api/lti/authorize_redirect',
clientId: '000000000000',
name: 'Local Canvas',
url: 'http://localhost:4000'
})
console.log(await plat.platformPublicKey())
lti.onConnect(
(connection, request, response) => {
console.log('connection', connection)
console.log('request', request)
console.log('response', response)
lti.redirect(response, 'http://lvh.me:3000/main')
},
{
secure: false
}
)
console.log('Deployed!')
}
here when I run the server I am getting the RSA key and I converted it then configure it in the tool on Canvas.
I tried to set secure to true and false but nothing is happening there. Then after ltijsFunction
I call
lti.app.get('http://lvh.me:3000/lti', (_req, res) => {
console.log('appUrl:', res.locals)
res.send('appUrl is alive')
})
lti.app.get('http://lvh.me:3000/main', (req, res) => {
console.log('hereee')
return res.send('It works!')
})
The Canvas configuration looks like this:
Redirect URIs: http://lvh.me:3000
Target Link Uri: http://lvh.me:3000
OpenID Connect Initiation Url: http://lvh.me:3000/lti
I also tried to move lti.onConnect
out of ltijsFunction
but same thing, I am not sure where is the problem.
Thanks
from ltijs.
@MahmoudAbdo90 Okay, i got it working with self hosted canvas. Launching and Grade generation are both working perfectly, later today i will comment here again detailing my setup and adapting your code to hopefully make it work.
I can say right now that your issues probably also come from how you are setting up your routes
ex: appUrl: 'http://lvh.me:3000/lti'
it would be somthing like this: appUrl: '/lti'
You dont have to specify the domain, the configurations expect only the route portion of the url.
Same thing here: lti.app.get('http://lvh.me:3000/lti' ...
it should be: lti.app.get('/lti' ...
and here: lti.redirect(response, 'http://lvh.me:3000/main')
=> lti.redirect(response, '/main')
also, you should not register a route with the same path as the "main appUrl" route, when you assign a route to appUrl, it is handled by lti.onConnect, if you then register a route with the same path, it may cause confusion. So you should delete this bit here:
lti.app.get('http://lvh.me:3000/lti', (_req, res) => {
console.log('appUrl:', res.locals)
res.send('appUrl is alive')
})
from ltijs.
@MahmoudAbdo90 Small update. I went ahead and created a keyset endpoint to facilitate registration within canvas, now you dont have to convert the RSA keys anymore. I'll upload the new version and update the documentation early tomorrow (29/12), as well as write the tutorial i talked about earlier today, and maybe some docker example images.
from ltijs.
@MahmoudAbdo90 Okay so here is the code i think should work:
import { Provider } from 'ltijs'
const lti = new Provider(
'Secret',
{ plugin: db },
{
appUrl: '/lti',
invalidTokenUrl: '/invalidtoken',
logger: true,
loginUrl: '/login',
sessionTimeoutUrl: '/sessionTimeoutUrl'
}
)
const ltijsFunction = async () => {
await lti.deploy()
const plat = await lti.registerPlatform({
accesstokenEndpoint: 'http://localhost:4000/login/oauth2/token',
authConfig: {
key: 'http://localhost:4000/api/lti/security/jwks',
method: 'JWK_SET'
},
authenticationEndpoint: 'http://localhost:4000/api/lti/authorize',
clientId: '000000000000',
name: 'Local Canvas',
url: 'https://canvas.instructure.com'
})
console.log(await plat.platformPublicKey())
lti.onConnect(
(connection, request, response) => {
console.log('connection', connection)
console.log('request', request)
console.log('response', response)
lti.redirect(response, '/main')
},
{
secure: false
}
)
console.log('Deployed!')
}
lti.app.get('/main', (req, res) => {
console.log('hereee')
return res.send('It works!')
})
ltijsFunction()
The canvas configuration should look like this:
Redirect URIs: http://lvh.me:3000/lti
Target Link Uri: http://lvh.me:3000/lti
OpenID Connect Initiation Url: http://lvh.me:3000/login
This should get the tool to at least launch.
from ltijs.
Hi, I looked into your nice tutorial, really helpful! ... but after trying some changes in my code I'm still struggling with it just like before. Then, I copied exactly the example code of yours, did a few minor modifications, and it still fails. Essentially, the changes were:
- when defining the provider, in particular the
/start
in the appUrl, whereappURL
itself is a prefix like/abcd/port/5679
. Just using.../
or...
never worked.
{
appUrl: `${appUrl}/start`,
loginUrl: `${appUrl}/login`,
sessionTimeoutUrl: `${appUrl}/sessionTimeout`,
invalidTokenUrl: `${appUrl}/invalidToken`
}
- I also changed
/main
and/grade
to have that prefix. So, all this seems to work, the provider's page with the grade button shows up, this shouldn't be the real issue... I also changed this in the html page, of course. - I have to change
{ secure: true }
, because when it is set tofalse
I get that invalid token error. - My final little change is to get the status of sending the grade, i.e.
const status = await lti.Grade.ScorePublish(res.locals.token, grade); return res.send("Grade Succesfully Created: " + status);
The problem is: the status
is false and no grade is recorded. The POST request returns Grade Succesfully Created: false
.
I'm really wondering what's going on. Maybe I'll just get rid of the moodle setup I have and hope a new one in the next year will help 😄 🙈
from ltijs.
@haraldschilly I am also facing the same thing, with Canvas and Moodle not sure what could be the reason though !
from ltijs.
@haraldschilly So you're able to launch the application, you just can't send a grade right? Can you run your code in debug mode like so DEBUG='provider:*' npm start
and show me what appears?
from ltijs.
@MahmoudAbdo90 With the updated code, are you able to launch to canvas?
from ltijs.
@haraldschilly I think I have an idea of what might be happening. What is the database plugin you are using? I think the database plugin is not rotating the access token, so you are getting an old one. I see the logs mention retrieving an access token, instead of generating a new one. I can fix this bug directly on the plugin.
from ltijs.
@Cvmcosta ah, interesting. well, I'm using your example code, from the blogpost, with a few modifications. Here is the setup:
// Creating a provider instance
const lti = new Lti(
"this_is_s3cr3t",
{
url: "mongodb://localhost/ltijs"
},
{
appUrl: `${appUrl}/start`,
loginUrl: `${appUrl}/login`,
sessionTimeoutUrl: `${appUrl}/sessionTimeout`,
invalidTokenUrl: `${appUrl}/invalidToken`
}
);
from ltijs.
In case it matters, it's the default mongodb from ubuntu 18.04, i.e. 3.6.3
(which is almost 2 years old)...
from ltijs.
@haraldschilly I see, you are using the default database plugin. So maybe there was an issue with that.
Can you reset (or change) the database to see if the error persists (ex: changing mongodb://localhost/ltijs
to mongodb://localhost/ltijs2
)? That would require also replacing the public key within moodle, since a new one would be generated when re-registering the platform.
But if you have direct access to the database (with NoSqlBooster for example) you can just delete the accesstokens table or the access tokens within it. If i am correct there should be an access token stored in it that should have been auto deleted some time ago.
from ltijs.
Hmm, I think I understand your idea, but no luck. I'm fine with working with mongo
on the command line. First, I ran use ltijs
and then db.dropDatabase()
.
Then I started it again and copied the public key as a moodle admin, then I switched to the course as a teacher and added the tool again (different name) and finally switched to be a student of that course.
Upon clicking the "submit grades" button for the first time, it looks like that:
provider:auth Cookie found +6s
provider:main Passing request to next handler +6s
provider:gradeService Target platform: https://moodletest.DOMAIN.com +6s
provider:gradeService Attempting to retrieve platform access_token for [https://moodletest.DOMAIN.com] +2ms
provider:platform Access_token found +6s
provider:gradeService Access_token retrieved for [https://moodletest.DOMAIN.com] +1ms
provider:gradeService Response code 401 (Unauthorized) +121ms
Then I run this in mongo db.accesstokens.deleteMany({})
and clicking that grade button again gives this (i.e. there is no access token and it generates a new one)
provider:auth Cookie found +13s
provider:main Passing request to next handler +13s
provider:gradeService Target platform: https://moodletest.DOMAIN.com +13s
provider:gradeService Attempting to retrieve platform access_token for [https://moodletest.DOMAIN.com] +1ms
provider:platform Access_token for https://moodletest.DOMAIN.com not found +13s
provider:platform Attempting to generate new access_token for https://moodletest.DOMAIN.com +0ms
provider:auth Awaiting return from the platform +19s
provider:auth Successfully generated new access_token +127ms
provider:gradeService Access_token retrieved for [https://moodletest.DOMAIN.com] +145ms
provider:gradeService Response code 401 (Unauthorized) +115ms
Somewhere, a detail must be missing (capabilities of what the tool can do?) or there is a bug with moodle. I don't get it.
from ltijs.
@Cvmcosta
When you used canvas did you pass it this way :
let plat = await lti.registerPlatform({
url: 'https://canvas.instructure.com',
name: 'Platform Name',
clientId: 'TOOLCLIENTID',
authenticationEndpoint: 'https://canvas.instructure.com/api/lti/authorization_redirect',
accesstokenEndpoint: 'https://canvas.instructure.com/login/oauth2/token',
authConfig: { method: 'JWK_SET', key: 'https://canvas.instructure.com/api/lti/security/jwks' }
})
or you used localhost ?
I am running my canvas self hosted on localhost:4000.
from ltijs.
@MahmoudAbdo90 I passed it like this:
const plat = await lti.registerPlatform({
url: 'https://canvas.instructure.com',
name: 'Local Canvas',
clientId: 'TOOLCLIENTID',
authenticationEndpoint: 'http://localhost:4000/api/lti/authorize',
accesstokenEndpoint: 'http://localhost:4000/login/oauth2/token',
authConfig: {
key: 'http://localhost:4000/api/lti/security/jwks',
method: 'JWK_SET'
},
})
The initial launch request canvas makes comes with the https://canvas.instructure.com
issuer, but all the subsequent requests should be made to your localhost:4000 instance. So the above code should work.
from ltijs.
@MahmoudAbdo90 And you should know that in order to register a grade with a lti tool on canvas, the tool should be added inside of an assignment, instead of standalone. This tutorial teaches you how to do this: Assignment using LTI app
from ltijs.
@Cvmcosta
I reached out the reason why my launch is not going through, there is an error logs in canvas './error_reports'
and it says:
Unknown Key Type:
category: JSON::JWK::UnknownAlgorithm
and here is my key after I converted it and added the missing values to it, do you have any idea what could be wrong with it ?
"e": "AQAB",
"n": "qHErINvmzcRquA5c74GT8vWcR7kWNOXc6wIxYBS1zB6bQFsuXAhHbHx5pJeZJIbSFiXX9Bbb5zICtY4DHrtHmHEKLHkk4U_7MgYWnUQN9NfYN0W_OQYlm-zR5dAk3JFONw7LnhxV6qOIWAJkGpjdNX0LxKXTh51u7Lwd_BTkltLK_2IDpXu_EfeFq2lw6lrJRn-6-fzKE8HN28MFgCs0AWb38-ls5N21qbyNB-06HM-ZGaR3Ok8Fjzz34VO9R-vcPhLci7iLhVD1nWfiDLQCl5FVXJ5DQv7JDUvVBXX5GB0bhW_-6zHuNleaQsYJFWVv1wIlhoI17IpDaQ347a9FZsJGseO6pYE66EL6OJouquG3X0byEr3DrSKuK4WvLoyDFwtQmQiOWR1b0wPLwiQMdm0atXS2g5yeXFGTbHG1Jj_z2XRILCHv7o2uF3GVELKfEi3j2_xs9R0gn3hRo1a__f6BKOeEuJtC8LKwwFe1CsB0f2igPwbOPekZgxYmilIfmmn0ggcc24RGXPDgg8KiA3dvIlEotdx0VkuScLLiOfk_AtUjkq9WcJpj_YhXmMiOZApTInGoWVJL6cm5a-akS45KUWGDwWns2ZFxbkS9jgRRF_A0FZ1gHgnUCgzCwCaBcFfI405O5Jv8sw_3Q-rslOSC3qOWomEdhHtJh1dMH_k",
"alg": "RS256",
"kid": "700f7577-64c5-403f-92af-e67e6782781d",
"kty": "RSA",
"use": "sig"
}
from ltijs.
@MahmoudAbdo90 I'll look into this more thoroughly this weekend, since i think i already encountered this bug and it had to do with the canvas installation, have you considered installing the stable version of Canvas through Docker (if that is not already the case), this was the version where everything worked for me.
Also i just released a new version of ltijs that has a keyset endpoint, to make it easier to work with Canvas. By default the keyset route is /keys
, and its usage requires you to register the platform again, since it relies on some new info added to the database when you register a new platform, you can do that by deleting the entry manually in the database or calling platform.remove ().
from ltijs.
Hey @Cvmcosta @MahmoudAbdo90 and @gentlemanjohn Im trying to set this up for a self hosted instance of canvas. unfortunately i can't seem to get a token back. i've noticed the code implementation has changed since the original posts at the start of the year. ill attach my code here if anyone is able to help. Im sure it has something to do with the JWK. ive read over the notes above and did the same thing by just copying what was in the canvas docs into the " public JWK". im sure thats the wrong implementation.
const path = require('path'); require('dotenv').config({ path: path.resolve(
.env`) });
const routes = require('./src/routes');
const lti = require('ltijs').Provider
console.log(process.env.DB_NAME)
lti.setup('api developer key',
{
url: 'mongodb+srv://username:[email protected]/?retryWrites=true&w=majority',
connection: { user: 'username', pass: 'password' }
}, {
staticPath: path.join(__dirname, './public'), // Path to static files
cookies: {
secure: true, // Set secure to true if the testing platform is in a different domain and https is being used
sameSite: 'None' // Set sameSite to 'None' if the testing platform is in a different domain and https is being used
},
devMode: false // Set DevMode to true if the testing platform is in a different domain and https is not being used
})
// Whitelisting the main app route and /nolti to create a landing page
lti.whitelist(lti.appRoute(), { route: new RegExp(/^/nolti$/), method: 'get' }) // Example Regex usage
// When receiving successful LTI launch redirects to app, otherwise redirects to landing page
lti.onConnect(async (token, req, res) => {
console.log("this is supposed to be the token " + token);
if (token) return res.sendFile(path.join(__dirname, './public/index.html'))
else lti.redirect(res, '/nolti') // Redirects to landing page
})
// When receiving deep linking request redirects to deep link React screen
lti.onDeepLinking(async (token, req, res) => {
return lti.redirect(res, '/deeplink', { newResource: true })
})
// Setting up routes
lti.app.use(routes)
// Setup function
const setup = async () => {
await lti.deploy({ port: process.env.PORT })
/**
- Register platform
*/
await lti.registerPlatform({
url: 'https://domain.test.instructure.com',
name: ' canvas instance',
clientId: 'api clientID',
authenticationEndpoint: 'https://domain.test.instructure.com/api/lti/authorize_redirect',
accesstokenEndpoint: 'https://domain.test.instructure.com/login/oauth2/token',
authConfig: { method: 'JWK_SET', key: 'https://domaintest.instructure.com/api/lti/security/jwks' }
})
}
setup()
`
from ltijs.
Hello @iceekreeam, the problem is most likely in the registration, both in the tool and in the platform. Here you can find links explaining the Canvas Setup process.
A quick summary:
- Canvas uses the same iss for most requests so your configuration would be something like:
await lti.registerPlatform({
url: 'https://canvas.test.instructure.com ',
name: ' canvas instance',
clientId: 'api clientID',
authenticationEndpoint: 'https://domain.test.instructure.com/api/lti/authorize_redirect',
accesstokenEndpoint: 'https://domain.test.instructure.com/login/oauth2/token',
authConfig: { method: 'JWK_SET', key: 'https://domaintest.instructure.com/api/lti/security/jwks' }
})
- Inside Canvas, choose to use JWK Keyset and paste your Tool's keyset endpoint
https://yourtool.com/keys
Can you share your Tool Configuration inside canvas?
from ltijs.
Hi @Cvmcosta thanks so much for a prompt reply. I'm really keen to get this working. I made a mistake earlier. My organisation's instance of canvas is actually hosted with instructure (not self hosted). I think the issue might be that i'm to request a token from a https:// instance of canvas while my ltijs demo server is on my local host http. I've been told that Canvas might reject requests in these circumstances. this might mean i have to host this test server on AWS lambda. in any case i have a screen shot of the tool config. Would be great to get your thoughts if there's anything else i can try
from ltijs.
It is possible that your requests are being blocked for being http, but i also spotted a couple of configuration issues in Canvas:
- Your target link URI is on port 3001 and your redirect URI is on port 3000, they should both be on the same port.
- Your OpenId Connect Initiation URI should be
http://localhost:3000/login
(or 3001).
from ltijs.
Hi @Cvmcosta originally i had the target link URI point at the test client App which was on port 3001. However i've made the changes you mentioned.
this might be a step closer, but i've screenshot the error that i'm now getting. I've also tried adding multiple redirect URIs which ive also took a screenshot but with the same result. My previous configuration did hit the onconnect function in the express server, but would redirect to the nolti page since i wasn't getting a token.
from ltijs.
I'd suggest trying to host this code to see if the http vs https issue is affecting something. It looks like your configuration is correct, did you delete and recreate the app after you updated the key settings? Canvas only applies the new setting to new apps.
My suggestions are:
- Delete the app and recreate it.
- Check your network tab to see what redirect_uri is being passed.
- Try to work on the same protocol http or https.
I'd test these in order and see if something changes. As i said, your configuration looks fine, so maybe it's just not being applied.
from ltijs.
Hi all -- was anyone ever able to solve this issue? I too am having difficulty posting grades to Canvas, and would love to see some working sample code. Thank you.
from ltijs.
Hello @bennytheshap, many times this issue come from a configuration mistake. Can you please show me your error logs so i can get a better idea of what is the problem. Also, what version of Ltijs are you using?
from ltijs.
I am lost in these discussions on where exactly the tool's keyset should go.
Tool's typical keyset url would be in this form:
JWKS URL: https://example.com/.well-known/jwks.json
I don't think it is something that platform provides and definitely not plat.platformPublicKey()
.
In the tutorial, it was suggested to get the key from the plat.platformPublicKey()
and paste it in the Moodle's public key. This cannot be right. Any thoughts?
from ltijs.
@GreenOn Hello, if you are talking about the Tool's keyset endpoint it is in the /keys
endpoint ( https://yourtool.com/keys
). I don't understand exactly what the issue is. When setting up the Tool inside moodle, you can either use the keyset endpoint, or use the raw RSA key returned from plat.platformPublicKey()
from ltijs.
Thanks. No issues. I think I am getting some clarity on this.
I think our tool's JWKS URL should go here..
{
url: 'mongodb://localhost/database',// Database url
connection:{ user:'user', pass: 'pass'}// Database configuration
},
{
appRoute: '/app',// Scpecifying main app route
loginRoute: '/loginroute', // Specifying login route
keysetRoute: '/.well-known/jwks.json', // Specifying keyset route
invalidTokenRoute: '/invalidtokenroute', // Specifying invalid token route
sessionTimeoutRoute: '/sessiontimeoutroute' // Specifying session timeout route
})
And for the key in the platform registration will always come from the platform provider.
url: 'http://localhost',
name: 'Localhost Moodle',
clientId: 'vnJik1AZWVdIs8A',
authenticationEndpoint:'http://localhost/mod/lti/auth.php',
accesstokenEndpoint:'http://localhost/mod/lti/token.php',
authConfig: {
method: 'JWK_SET',
key: 'http://localhost/mod/lti/certs.php' }
})
I think using the verbiage like JWKS or well-known URL's in the documentation can clear the confusion out.
from ltijs.
@GreenOn In the setup method, keysetRoute
just creates an express route that serves the JWKS, does setting the route path as /.well-known/jwks.json
work?
from ltijs.
@Cvmcosta , I am getting ready to deploy this to our sever and test it. if that keysetRoute
doesn't work, we will need to figure out something. Typically, services like Auth0, OKTA provide these JWKS URL's to use. I can get back to you soon on how it goes.
from ltijs.
But Ltijs does provide you with a JWKS URL, by default is /keys
, the only thing keysetRoute
does is change the default route path.
If you got to http://yourtool.com/keys
you should see the JWK set.
from ltijs.
I see. So, would there be anyway for us to overwrite the URL given by Ltijs with our tool's current JWKS URL?
from ltijs.
I think that these JWKS URLs have different purposes, /keys
will only be used by LMSs since all it has it's the platform registration public keys. Ltijs generates a key pair for each registered platform, then when a platform has to validate a JWT, it hits the keys
endpoint to get the correct public key and verify the signature.
from ltijs.
We use Auth0 and would like to integrate with the existing authentication framework. When we asked IMS Global, here's what they suggested. I am still scratching my head to see how I can accomplish this.
from ltijs.
@Cvmcosta hey guys so i still haven't finished my testing but i did host the ltijs server on heroku so that both the LMS and the tool are on HTTPS. I can see that Canvas did eventually generate a token for me however theres still more work i need to do before im clear about any remaining issues. One thing i noticed is the LTIJS server picked up the target_uri from the initial launch and sent it back to the platform as the redirect_uri which is incorrect. I think i can manipulate it to work but i will get back to you all
from ltijs.
@iceekreeam can you tell me what exactly happened with the redirect_uri situation? Ltijs has a system in place where it strips the target link uri of query parameters and adds them back after the request is finished. That is done because some LMSs follow the oauth specification strictly, which does not allow dynamic parameters on the redirect_uri. And in doing so I can get Ltijs to work seemlessly between LMSs.
from ltijs.
@Cvmcosta so i listed my target uri as also one of the redirect uris and thats what got picked up by ltijs as the redirect_uri when it sent a response back to canvas in step 2 of the open id connect process. does that make sense?
from ltijs.
It could be a mistake from my end but, i have generated an auth token from canvas listed below. At this point i assume i need to also upload your demo client on heroku and see if the whole process works smoothly. Another random issue im running into is that i couldn't generate the developer key with the same settings in production environment of canvas.. so i still have some work on my end
from ltijs.
@iceekreeam I might be misunderstanding this somehow but i still dont see an issue with the redirect_uri, Ltijs sets the the final endpoint as the redirect_uri since it`s where the LMS should redirect to at the end of the login flow.
I also had some issues with the production environment of Canvas but it was due to installation errors.
from ltijs.
@Cvmcosta Hi, no your probably right, im just trying to navigate my way through this. I'm not really certain if there was a definite issue. At this point my goal is to replicate deep linking, Am i correct in thinking i need to setup the Client demo Server and have that be my final redirect URI? Are your two demo apps designed to interact with each other?
from ltijs.
@iceekreeam Sorry i completely forgot about this issue and was only reminded of it because it was cited in another one.
I don't know if you are still having this issue, but i think it's worth answering as it might help someone else. Your final redirect URI should be the same as the root where your app is running, example: http://localhost:3000
. And the Demo Client is built into the Demo Server, so you don't have to run both of them.
from ltijs.
I'll be closing this issue since it has now long deviated from the original topic. Any new problems should be reported in a new Issue.
Ltijs integration with Canvas is working perfectly and i've learned a lot about the possible issues over the last couple of months and most of them are due to misconfiguration or other small mistakes. I am working on a FAQ section for Ltijs that will cover most of the common errors.
from ltijs.
Related Issues (20)
- After sucessful login request self hosted canvas is sending request to canvas.docker instead of the hosted URL HOT 2
- Error obtaining the roles
- /members and /grade endpoints are not working. They are throwing 500 error. HOT 5
- Update @types Repository or Migrate to TypeScript HOT 2
- Error on D2L LMS only
- Provide possibility to customize the bodyParser configuration
- /keys URL breaks if multiple LTIs are registered - error:1C800064:Provider routines::bad decrypt HOT 3
- Help Needed: "No Ltik or ID Token found." HOT 1
- unable to verify the first certificate issue HOT 1
- Unable to select content while Adding a External tool
- Getting this error when accessing the namesandroles and grades HTTPError: Internal Server Error HOT 1
- "MISSING_LOGIN_PARAMETERS" error : cookie issue?
- Provide opportunity to override `redirect_uri` in `Request.ltiAdvantageLogin`
- Feature Support: Submissions Review Service HOT 1
- Correct RS256 keypair keeps throwing: secretOrPrivateKey must be a symmetric key when using HS256
- Plans for the support of browsers disabling third-party cookies HOT 6
- Access the custom fields during launch HOT 1
- Potential concern of users viewing ltijs as an LTI "shim"
- Enhanced Database and Data Management Capabilities for Ltijs
- DeepLink error only in Moodle v 4.3 HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ltijs.