Open abdullah-abusall opened 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.
@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
@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:
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...):
@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.
@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
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:
Here is the error screen using an instructor account (which exists on the same AAD) to launch the LTI 1.1
The launch request:
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<i_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
We also faced the same issue when we converted the same user to a learner instead of an instructor
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":
Here is the requested URL when we try to hit it manually:
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" ] } ] } } }
We are still receiving the 500 response error even through we are using a user that exists on the same AAD:
and here is the error we found in the "connect" function app log stream:
We also made sure that nothing is wrong with our NRPS that we provide the url of in the JWT sent above with the launch request "id_token"
We made sure of that testing using the provided imsglobal test tools:
You could see in the launch results that the users data were fetched successfully and how we provide the url for our NRPS:
Here is the launch details: https://lti-ri.imsglobal.org/lti/tools/1891/launches/15384
Also here are the NRPS request and response:
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:
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": [] } ] }
@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.
@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.
@leestott great news we were able to launch the LTI 1.3
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:
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?
@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
@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:
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:
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.
@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
@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.
@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
@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
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.
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: Could you guys just confirm that if its true please.