dmitry-viskov / pylti1.3

LTI 1.3 Advantage Tool
MIT License
122 stars 64 forks source link

Trouble with using nrps and canvas #48

Open joerou opened 3 years ago

joerou commented 3 years ago

Hello,

Awesome package!

I'm using this package with fastapi and am working on an integration with canvas. I am able to launch the tool just fine and also to deep link a resource and launch that - but whenever I try to use one of the "services" such as nrps (specifically here the get_members() function) I am getting the following error pylti1p3.exception.LtiServiceException: HTTP response [https://canvas.instructure.com/login/oauth2/token]: 400 - {"error":"invalid_request","error_description":"JWS signature invalid."}

more complete stack:

File "/usr/local/lib/python3.7/site-packages/pylti1p3/names_roles.py", line 66, in get_members

members, members_url = self.get_members_page(members_url)

File "/usr/local/lib/python3.7/site-packages/pylti1p3/names_roles.py", line 50, in get_members_page

accept='application/vnd.ims.lti-nrps.v2.membershipcontainer+json',

File "/usr/local/lib/python3.7/site-packages/pylti1p3/service_connector.py", line 107, in make_service_request

access_token = self.get_access_token(scopes)

File "/usr/local/lib/python3.7/site-packages/pylti1p3/service_connector.py", line 84, in get_access_token

raise LtiServiceException(r)

pylti1p3.exception.LtiServiceException: HTTP response [https://canvas.instructure.com/login/oauth2/token]: 400 - {"error":"invalid_request","error_description":"JWS signature invalid."}

I have tested the same setup with this platform testing tool https://saltire.lti.app/platform and nrps works fine I am able to get the member list just fine. I'm not sure where to look next to debug..

Any help would be greatly appreciated!

Thanks, Joe

joerou commented 3 years ago

oh I just found if I paste the jwk into canvas rather than use the url then everything seems to work fine...

My fastapi endpoint for the public jwks looks like

@router.get("/.well-known/jwks.json", response_class=JSONResponse)
def jwks():
    tool_conf = ToolConfJsonFile('/app/app/api/api_v1/lti_config.json')
    return {'keys': tool_conf.get_jwks()}

Perhaps I'm missing a header that needs to be set or something?

dmitry-viskov commented 3 years ago

hi @joerou ! Sorry for delay with the answer. Initially before calling Platform's API LTI tool tries to get access token from the platform if someone tries to use any Advantage service (Names and Roles / Assignment and Grade Services and etc). LTI tool uses private key to encode the auth request message. LTI platform uses one of the available public keys to decode this message. Usually this group of public keys is available from the special public URL (like /.well-known/jwks.json from your example). Authorization isn't needed here because keys are public. More rarely LMS-es allow to set public key as static value as JWK in case of Canvas but this is an undesirable way. Your error happened because platform could not fetch public key from the URL. I may recommend to check URL availability and check HTTP status code (must be 200). Also returnable public key must match the used private key.

joerou commented 3 years ago

Hello,

No worries about the delay!

I'm not sure if maybe there is another error - my jwks.json is available publicly (https://noodle-case-lb.herokuapp.com/api/v1/.well-known/jwks.json) but seems not to work and when I paste the key in manually it does work. It's a bit weird because it did work with the Saltire testing website but just not with canvas...

I'm also curious if you are planning to make a fastapi connector in the library itself? I did make my own and would be happy to make the initial PR but I think it will need a bit of cleaning up - if that's interesting let me know!

Again awesome work on this!

Thanks, Joe

dmitry-viskov commented 3 years ago

hi @joerou

It looks very odd and seems like a Canvas internal issue. Did you check requests from Canvas to this JWKS API in the access.log? Are they exist?

I'm also curious if you are planning to make a fastapi connector in the library itself?

There was a PR with such adapter: https://github.com/dmitry-viskov/pylti1.3/pull/47 and I described all reasons why it was closed here: https://github.com/dmitry-viskov/pylti1.3/pull/47#issuecomment-853101878 I think we will return to this question at least after python 2.7 support will be dropped

joerou commented 3 years ago

Hey,

Excuses for the late reply. Thanks for the information and that makes sense, I'm happy to assist once you've dropped python 2.7 support if you are interested at that point in a fastapi integration.

I have been working on other parts of the application and haven't tried to pinpoint the source of the error here as I was able to get it working by directly pasting in the public key..

The endpoint does work, is publicly accessible and canvas is able to hit it successfully (showing a 200 response on the server side). I'm not sure if it's an issue with the way I've formatted it or not. I'll keep you posted when I get back into this error.

thanks, Joe

hmoffatt commented 2 years ago

Same as #61 . There is an error in the JWKS endpoint in the Flask demo.