microsoftarchive / Learn-LTI

Access the Microsoft Learn http://docs.microsoft.com/learn Catalog of Learning Paths and Modules directly from your Learning Management Systems using the Microsoft Learn LTI application
https://microsoft.github.io/Learn-LTI/
MIT License
126 stars 50 forks source link

Classera.com - MultiTennant Domain Support for Learn LTI Application #134

Open abdullah-abusall opened 3 years ago

abdullah-abusall commented 3 years ago

When we tried to integrate using LTI v1.3 the page returns 500 and looking at the log this is the error we are getting: 2021-05-27T13:18:29.223 [Error] Executed 'LtiAdvantageLaunch' (Failed, Id=74ebc0dc-b2c6-412a-8008-3851238137ae, Duration=1862ms)Object reference not set to an instance of an object. image image

and when we tried to integrate using LTI v1.0 it looks like the tool will need us to provide an AD account for all of the academies users since we are receiving the "You are not enrolled in this course" error: image Could you guys just confirm that if its true please.

leestott commented 3 years ago

@abdullah-abusall Can you check you have a Azure Active Directory connected to your users.

Check AAD, and NRPS is working.

We have seen recent issues where users emails in your LMS and AAD do not match you can receive a failure with the the tool as the email address with which the user was enrolled in the course was not the same as the AAD email- the casing being different.

Please ensure the email are the same and the case matches of the email.

Please let us know if your issue is related to user accounts being different in AAD vs your LMS.

leestott commented 3 years ago

@abdullah-abusall please also check. That your using a valid 3rd party SSL certificate for your LMS environment as we only support passing of information via a secure http method.

Please confirm if this help unblock your issues please see the troubleshooting page for more details https://github.com/microsoft/Learn-LTI/blob/main/docs/TROUBLESHOOTING.md

abdullah-abusall commented 3 years ago

@leestott thanks for your response! but another concern of ours does the deployment under the same roof with the users tenant? we use an entirely different azure tenet/account for our servers than from what we use to manage users:

Screen Shot 2021-06-06 at 8 47 43 AM

Plus for the LTI 1.3 integration we still the same issue with the 500 error and nothing seems to be wrong with our NRPS; we are testing our LTI integration with the tools provided by imsglobal and everything seems to be working fine integrating with them (users data are fetched and everything...):

Screen Shot 2021-06-06 at 9 29 57 AM
leestott commented 3 years ago

@abdullah-abusall

So you need to deploy app within the same AAD tenant as your users.

This is to enable the users to login and can be validated as a valid users of the app.

If you not using validated users you will receive the error as you have above as the user cannot validate with the app.

The users AAD email address needs to match the LMS users emails/login to validate they are the same user.

leestott commented 3 years ago

@abdullah-abusall did you manage to to get the installed and working across your multiple AAD infrastructure?

The issue is the Learn LTI Application is registered within the AAD Tenant so only users within that Tenant have access to those resoruces https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals and you need to setup Application multitenant identity https://docs.microsoft.com/en-us/azure/architecture/multitenant-identity/

In this guidance, we'll look specifically at using Azure AD for identity management.

If you have on-premises Active Directory you can use Azure AD Connect to sync your on-premises Active Directory with Azure AD. If your on-premises Active Directory cannot use Azure AD Connect (due to corporate IT policy or other reasons), the SaaS provider can federate with the customer's directory through Active Directory Federation Services (AD FS). see https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant

abdullah-abusall commented 3 years ago

Thank you @leestott We agreed with the team today that we would finish up fixing the issue on the same tenant (app deployed to same tenant users exists on) before we move on to see how we could handle it across multi-tenants

I'll make two detailed comments showing the issue we are currently having for both LTI 1.1 and LTI 1.3:

Starting with 1.1 since it seems its the one who should be easier to fix:

curl 'https://connect-tff3sauac.azurewebsites.net/api/launch-lti1' \ -H 'Connection: keep-alive' \ -H 'Cache-Control: max-age=0' \ -H 'sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'Upgrade-Insecure-Requests: 1' \ -H 'Origin: https://csva.classera.com' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36' \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \ -H 'Sec-Fetch-Site: cross-site' \ -H 'Sec-Fetch-Mode: navigate' \ -H 'Sec-Fetch-User: ?1' \ -H 'Sec-Fetch-Dest: document' \ -H 'Referer: https://csva.classera.com/' \ -H 'Accept-Language: en-US,en;q=0.9,fr;q=0.8,es;q=0.7' \ --data-raw 'lti_message_type=basic-lti-launch-request&lti_version=LTI-1p0&resource_link_id=ODIxOQ%3D%3D&resource_link_title=Microsoft+1.1&user_id=NQ%3D%3D&context_id=Mzg%3D&context_title=big+image+test&context_label=Lecture&launch_presentation_return_url=https%3A%2F%2Fcsva.classera.com%2Fattachments-details%2F8219%2FMicrosoft%25201.1%2Factivity%2Fbig%2520image%2520test%2F11%2F38%2F&oauth_consumer_key=LearnLTI&oauth_nonce=MTYyNzg5NTI2Ng%3D%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1627895266&oauth_version=1.0&tool_consumer_instance_guid=csva.classera.com&tool_consumer_instance_name=Test+School+1&tool_consumer_instance_contact_email=info%40classera.com&lis_person_name_given=Abdullah&lis_person_name_family=Abdullah&lis_person_name_full=Abdullah&lis_person_contact_email_primary=a.abusall%40classera.com&ext_roles=urn%3Alti%3Arole%3Aims%2Flis%2FInstructor&roles=Instructor&custom_context_memberships_url=https%3A%2F%2Fsvaapi.classera.com%2FLtiProviders%2Fmembership%3Faccess_token%3Da3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709&oauth_signature=OrCaVCaDb%2BrUb130UP%2FEK6aB5MI%3D' \ --compressed

URL: https://svaapi.classera.com/LtiProviders/membership?access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709&rlid=ODIxOQ==

Response: { "@context": [ "http://purl.imsglobal.org/ctx/lis/v2/MembershipContainer", { "liss": "http://purl.imsglobal.org/vocab/lis/v2/status#", "lism": "http://purl.imsglobal.org/vocab/lis/v2/membership#" } ], "@type": "Page", "@id": "https://svaapi.classera.com/LtiProviders/membership?access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709", "pageOf": { "@type": "LISMembershipContainer", "membershipSubject": { "@type": "Context", "@id": "https://svaapi.classera.com/LtiProviders/membership?access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709", "contextId": "ODIxOQ==", "membership": [ { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTA=", "sourcedId": "MTA=", "name": "Ayman", "familyName": "Ayman", "givenName": "Ayman", "email": "abusall@classera.com" }, "role": [ "lism:Learner" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "NzU1MDg=", "sourcedId": "NzU1MDg=", "name": "Importing with fielddsss1", "familyName": "Importing with fielddsss1", "givenName": "Importing with fielddsss1", "email": "importing_with_fielddsss1@cls.com" }, "role": [ "lism:Learner" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "NzU1MTA=", "sourcedId": "NzU1MTA=", "name": "Importing with fielddsss", "familyName": "Importing with fielddsss", "givenName": "Importing with fielddsss", "email": "logede7793@sc2hub.com" }, "role": [ "lism:Learner" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "NQ==", "sourcedId": "NQ==", "name": "Abdullah", "familyName": "Abdullah", "givenName": "Abdullah", "email": "a.abusall@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "Nw==", "sourcedId": "Nw==", "name": "School Admin", "familyName": "School Admin", "givenName": "School Admin", "email": "schadmin@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTEyOTI=", "sourcedId": "MTEyOTI=", "name": "schadmin2", "familyName": "schadmin2", "givenName": "schadmin2", "email": "schadmin2@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTEyOTM=", "sourcedId": "MTEyOTM=", "name": "schadmin3", "familyName": "schadmin3", "givenName": "schadmin3", "email": "schadmin3@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTEyOTQ=", "sourcedId": "MTEyOTQ=", "name": "schadmin4", "familyName": "schadmin4", "givenName": "schadmin4", "email": "schadmin4@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTAzMTQ4", "sourcedId": "MTAzMTQ4", "name": "schadmin5", "familyName": "schadmin5", "givenName": "schadmin5", "email": "schadmin5@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTAzMTQ5", "sourcedId": "MTAzMTQ5", "name": "schadmin6", "familyName": "schadmin6", "givenName": "schadmin6", "email": "schadmin6@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTE0MjY3", "sourcedId": "MTE0MjY3", "name": "schadmin7", "familyName": "schadmin7", "givenName": "schadmin7", "email": "schadmin7@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTE0MjY4", "sourcedId": "MTE0MjY4", "name": "schadmin8", "familyName": "schadmin8", "givenName": "schadmin8", "email": "schadmin8@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTE0MjY5", "sourcedId": "MTE0MjY5", "name": "schadmin9", "familyName": "schadmin9", "givenName": "schadmin9", "email": "schadmin9@classera.com" }, "role": [ "lism:Instructor" ] }, { "status": "liss:Active", "member": { "@type": "Person", "userId": "MTE1NjAw", "sourcedId": "MTE1NjAw", "name": "schadmin10", "familyName": "schadmin10", "givenName": "schadmin10", "email": "schadmin10@classera.com" }, "role": [ "lism:Instructor" ] } ] } } }

abdullah-abusall commented 3 years ago

As for the LTI 1.3

URL: https://svaapi.classera.com/LtiProviders/nrps?ac=1&pgn=4&rc=8030&access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709

Response: { "id": "https://svaapi.classera.com/LtiProviders/nrps?ac=1&pgn=4&rc=8030&access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709", "members": [ { "status": "Active", "name": "Ayman", "given_name": "Ayman", "family_name": "Ayman", "email": "abusall@classera.com", "user_id": 1656398468, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Importing with fielddsss1", "given_name": "Importing with fielddsss1", "family_name": "Importing with fielddsss1", "email": "importing_with_fielddsss1@cls.com", "user_id": 401017902, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Importing with fielddsss", "given_name": "Importing with fielddsss", "family_name": "Importing with fielddsss", "email": "logede7793@sc2hub.com", "user_id": 2104050624, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Abdullah", "given_name": "Abdullah", "family_name": "Abdullah", "email": "a.abusall@classera.com", "user_id": 476726705, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "School Admin", "given_name": "School Admin", "family_name": "School Admin", "email": "schadmin@classera.com", "user_id": 163870807, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin2", "given_name": "schadmin2", "family_name": "schadmin2", "email": "schadmin2@classera.com", "user_id": 1296063160, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin3", "given_name": "schadmin3", "family_name": "schadmin3", "email": "schadmin3@classera.com", "user_id": 5782015, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin4", "given_name": "schadmin4", "family_name": "schadmin4", "email": "schadmin4@classera.com", "user_id": 2136256, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin5", "given_name": "schadmin5", "family_name": "schadmin5", "email": "schadmin5@classera.com", "user_id": 1530645160, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin6", "given_name": "schadmin6", "family_name": "schadmin6", "email": "schadmin6@classera.com", "user_id": 672925089, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin7", "given_name": "schadmin7", "family_name": "schadmin7", "email": "schadmin7@classera.com", "user_id": 1520044817, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin8", "given_name": "schadmin8", "family_name": "schadmin8", "email": "schadmin8@classera.com", "user_id": 1118922114, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin9", "given_name": "schadmin9", "family_name": "schadmin9", "email": "schadmin9@classera.com", "user_id": 142651505, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin10", "given_name": "schadmin10", "family_name": "schadmin10", "email": "schadmin10@classera.com", "user_id": 1930371125, "roles": [ "Instructor" ], "message": [] } ] }

and here are the NRPS details for the request we provide to the Microsoft learn launch:

Screen Shot 2021-08-02 at 1 03 14 PM

URL: https://svaapi.classera.com/LtiProviders/nrps?ac=1&pgn=5&rc=8218&access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709

Response:

{ "id": "https://svaapi.classera.com/LtiProviders/nrps?ac=1&pgn=5&rc=8218&access_token=a3d8c21b54c8ad8e4cc0d7d066b476c2ce9da709", "members": [ { "status": "Active", "name": "Ayman", "given_name": "Ayman", "family_name": "Ayman", "email": "abusall@classera.com", "user_id": 1656398468, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Importing with fielddsss1", "given_name": "Importing with fielddsss1", "family_name": "Importing with fielddsss1", "email": "importing_with_fielddsss1@cls.com", "user_id": 401017902, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Importing with fielddsss", "given_name": "Importing with fielddsss", "family_name": "Importing with fielddsss", "email": "logede7793@sc2hub.com", "user_id": 2104050624, "roles": [ "Learner" ], "message": [] }, { "status": "Active", "name": "Abdullah", "given_name": "Abdullah", "family_name": "Abdullah", "email": "a.abusall@classera.com", "user_id": 476726705, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "School Admin", "given_name": "School Admin", "family_name": "School Admin", "email": "schadmin@classera.com", "user_id": 163870807, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin2", "given_name": "schadmin2", "family_name": "schadmin2", "email": "schadmin2@classera.com", "user_id": 1296063160, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin3", "given_name": "schadmin3", "family_name": "schadmin3", "email": "schadmin3@classera.com", "user_id": 5782015, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin4", "given_name": "schadmin4", "family_name": "schadmin4", "email": "schadmin4@classera.com", "user_id": 2136256, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin5", "given_name": "schadmin5", "family_name": "schadmin5", "email": "schadmin5@classera.com", "user_id": 1530645160, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin6", "given_name": "schadmin6", "family_name": "schadmin6", "email": "schadmin6@classera.com", "user_id": 672925089, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin7", "given_name": "schadmin7", "family_name": "schadmin7", "email": "schadmin7@classera.com", "user_id": 1520044817, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin8", "given_name": "schadmin8", "family_name": "schadmin8", "email": "schadmin8@classera.com", "user_id": 1118922114, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin9", "given_name": "schadmin9", "family_name": "schadmin9", "email": "schadmin9@classera.com", "user_id": 142651505, "roles": [ "Instructor" ], "message": [] }, { "status": "Active", "name": "schadmin10", "given_name": "schadmin10", "family_name": "schadmin10", "email": "schadmin10@classera.com", "user_id": 1930371125, "roles": [ "Instructor" ], "message": [] } ] }

leestott commented 3 years ago

@abdullah-abusall so what is the LMS your using? https://learn.classera.com/ The Users trying OpenEdx seem to have similar issues https://github.com/microsoft/Learn-LTI/issues/103

At present we have only tested the solution with Moodle, Canvas and Blackboard.

abdullah-abusall commented 3 years ago

@leestott the url for the LMS we are testing on is https://csva.classera.com/ (its a test domain/academy so please don't mind all the rubbish data) and yes its a custom built LMS

Looking at OpenEdx thread it seems that the issue here is with the membership url not returning the members from Edx it-self and as you could see from my comments our NRPS are returning successfully.

But I will also try debugging the function the same way mentioned there and see how the results are received and interpreted.

abdullah-abusall commented 3 years ago

@leestott great news we were able to launch the LTI 1.3

Screen Shot 2021-08-03 at 4 51 02 PM Screen Shot 2021-08-03 at 4 50 46 PM Screen Shot 2021-08-03 at 4 50 37 PM Screen Shot 2021-08-03 at 4 49 06 PM

The (LtiAdvantageLaunch 500) error was caused by us not providing the ResourceLink and the Context titles in the JWT payload Object reference not set to an instance of an object.

As for the LTI 1.1 We fixed the previously reported issue:

Looking through the log stream in the "users" app function on azure we found that the app is trying to fetch the user data from the "membership" url that we provide with the launch payload then it will say "Unauthorized" even though when we request the same "membership url" the user data are returned with all the users that are part of this requested "resource_link_id"

by making sure the "Authorization" header was accepted from our side.

After that we ran into another issue were it just said:

Screen Shot 2021-08-03 at 5 00 20 PM Screen Shot 2021-08-03 at 4 59 51 PM Screen Shot 2021-08-03 at 5 08 41 PM

I looked through the repo to find how it parses the membership to make sure it goes with the standards: https://www.imsglobal.org/lti/model/mediatype/application/vnd/ims/lis/v2/membershipcontainer%2Bjson/index.html but I couldn't find it

anyhow the LTI 1.3 is what we really need and that now it's running can you please provide us with more details on how to handle the multi tenant situation? Like I couldn't find the app we need to configure to handle multi tenant authorization were can we find the app step in the deployment?

leestott commented 3 years ago

@abdullah-abusall Awesome!!

So to make the app support Azure B2C you need to change the code OK so you simply need to edit this file https://github.com/microsoft/Learn-LTI/blob/main/client/src/Core/Auth/AppAuthConfig.ts

Line 11 authority: https://login.microsoftonline.com/${process.env.REACT_APP_EDNA_TENANT_ID},

to authority: https://login.microsoftonline.com/common/${process.env.REACT_APP_EDNA_TENANT_ID},

see https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant for other details

abdullah-abusall commented 3 years ago

@leestott unfortunately we ran into a new issue

We updated the code line mentioned above but the new value had to be https://login.microsoftonline.com/common/ not https://login.microsoftonline.com/common/${process.env.REACT_APP_EDNA_TENANT_ID}, since that last one got us this error:

Screen Shot 2021-08-05 at 3 01 03 PM Screen Shot 2021-08-05 at 3 09 59 PM

We also updated the app "Supported account types" to :

Accounts in any organizational directory (Any Azure AD directory - Multitenant)

but after that we ran into this issue:

Screen Shot 2021-08-05 at 3 19 49 PM

and the troubleshooting only describe this error at the stage of deploying the application which doesn't apply here.

also this error now appears for both users on the same tenant and on a different one.

leestott commented 3 years ago

@abdullah-abusall

So please look at this best practice for making the application support this scenario https://docs.microsoft.com/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant

There a few additional changes you will need to make this is a good overview video https://www.youtube.com/watch?v=Jfrp7DI7G0Q

abdullah-abusall commented 3 years ago

@leestott Thanks for the links and I believe we have updated configured the app correctly but we still get the same error. are there any additional code changes we need to make?

I suspect that the line: "issuer": "[concat('https://sts.windows.net/', variables('tenantId'), '/')]", in deployment/azuredeploy.json needs to be updated? but I couldn't track where that is used and how.

leestott commented 3 years ago

@abdullah-abusall

Yes your correct the issue you have is that the solution has been design for deployment in managed tenant environments where the admin knows the tenant IDs if your deploying this as SaaS for users to self configure then this isnt going to work at present.

We have found a work around which will solve the customer who have ownership and deployment of both the Learn LTI Application and the AAD tenants which users are located

If the customers follow this work around, we suggest they undergo a security review on their end.

The customers can make the following changes on their end after forking/cloning the repository and then deploy.

Note – These steps are not a workaround for proper Multi-Tenant behavior.

It is only a workaround for the above customer issue as the mentioned this in the comments - we use an entirely different azure tenet/account for our servers than from what we use to manage users here https://github.com/microsoft/Learn-LTI/issues/134#issuecomment-855347435

Steps: • In the Install-Client.ps1 file, Replace REACT_APP_EDNA_TENANT_ID="$(Get-TenantId $AppId)"; with REACT_APP_EDNA_TENANT_ID="TID"; where TID is the tenant id with intended users • In the azuredeploy.json file, Remove line no.143 , "issuer": "[concat('https://sts.windows.net/', variables('tenantId'), '/')]" • In the azuredeploy.json file, update line no.234 with the user email address of the admin from the tenant (TID), "AllowedUsers": "user@newTenant", • Run the deployment script. • Make the app multitenant in the azure portal. • While Creating platform registration, login with with upn from tenant TID

leestott commented 2 years ago

@ahmedhosameldein if you would like to test AAD B2C support please use this fork https://github.com/microsoft/Learn-LTI/issues/149 https://github.com/UCL-MSc-Learn-LTI/Learn-LTI