Hi all — I’m implementing LTI 1.3 Deep Linking. When I POST the Deep Linking Response to the deep_link_return_url, Canvas returns 200 OK, but:
Environment
-
Canvas cloud
-
Tool issuer: <TOOL_ISSUER> (e.g., <CODE data-start="524" data-end="550"><A href="https://tool.example.com" target="_blank" rel="noopener nofollow noreferrer">https://tool.example.com)</A>
-
Client ID: <CLIENT_ID>
-
Deployment ID: <DEPLOYMENT_ID>
-
</CODE><SPAN>JWKS URL: </SPAN><CODE data-start="626" data-end="638"><JWKS_URL>
Request I’m sending
URL (exact): /courses//deep_linking_response?data=" target="_blank" rel="nofollow noopener noreferrer">https:///courses//deep_linking_response?data=
Method: POST (application/x-www-form-urlencoded)
Form fields:id_token= (no state present in this DL flow)<CODE data-start="887" data-end="894">
Deep Linking Response (JWT)
<SPAN class="">{</SPAN>
<CODE class=""><SPAN><SPAN class="">"iss"<SPAN class="">: <SPAN class="">"<TOOL_ISSUER>"<SPAN class="">, <SPAN class="">"aud"<SPAN class="">: <SPAN class="">"<CLIENT_ID>"<SPAN class="">, <SPAN class="">"azp"<SPAN class="">: <SPAN class="">"<CLIENT_ID>"<SPAN class="">, <SPAN class="">"sub"<SPAN class="">: <SPAN class="">"<TOOL_USER_ID>"<SPAN class="">, <SPAN class="">"iat"<SPAN class="">: <unix><SPAN class="">, <SPAN class="">"exp"<SPAN class="">: <unix><SPAN class="">, <SPAN class="">"<A href="https://purl.imsglobal.org/spec/lti/claim/message_type" target="_blank" rel="noopener nofollow noreferrer">https://purl.imsglobal.org/spec/lti/claim/message_type"<SPAN class="">: <SPAN class="">"LtiDeepLinkingResponse"<SPAN class="">, <SPAN class="">"</SPAN></SPAN></SPAN></SPAN></A><A href="https://purl.imsglobal.org/spec/lti/claim/version" target="_blank" rel="noopener nofollow noreferrer">https://purl.imsglobal.org/spec/lti/claim/version"<SPAN class="">: <SPAN class="">"1.3.0"<SPAN class="">, <SPAN class="">"</SPAN></SPAN></SPAN></SPAN></A><A href="https://purl.imsglobal.org/spec/lti/claim/deployment_id" target="_blank" rel="noopener nofollow noreferrer">https://purl.imsglobal.org/spec/lti/claim/deployment_id"<SPAN class="">: <SPAN class="">"<DEPLOYMENT_ID>"<SPAN class="">, <SPAN class="">"</SPAN></SPAN></SPAN></SPAN></A><A href="https://purl.imsglobal.org/spec/lti-dl/claim/data" target="_blank" rel="noopener nofollow noreferrer">https://purl.imsglobal.org/spec/lti-dl/claim/data"<SPAN class="">: <SPAN class="">"<OPAQUE_DATA_FROM_URL>"<SPAN class="">, <SPAN class="">"</SPAN></SPAN></SPAN></SPAN></A><A href="https://purl.imsglobal.org/spec/lti-dl/claim/content_items" target="_blank" rel="noopener nofollow noreferrer">https://purl.imsglobal.org/spec/lti-dl/claim/content_items"<SPAN class="">: <SPAN class="">[ <SPAN class="">{ <SPAN class="">"type"<SPAN class="">: <SPAN class="">"ltiResourceLink"<SPAN class="">, <SPAN class="">"title"<SPAN class="">: <SPAN class="">"<SHORT_TITLE>"<SPAN class="">, <SPAN class="">"url"<SPAN class="">: <SPAN class="">"<TARGET_LINK_URI>"<SPAN class="">, <SPAN class="">"id"<SPAN class="">: <SPAN class="">"resource-<UUID>"<SPAN class="">, <SPAN class="">"text"<SPAN class="">: <SPAN class="">"Optional description" <SPAN class="">} <SPAN class="">] <SPAN class="">}</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></A></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN>
Expected
Canvas closes the chooser and adds the LTI link (assignment/resource) to the course.
Actual
POST returns 200, but no UI change and chooser stays open.
Questions
-
Are there additional required fields for ltiResourceLink in Canvas for the assignment_selection placement?
-
Must content_items[0].url exactly equal the registered target_link_uri in the Developer Key?
-
Any course/role/placement settings that could accept the response (200) but not place content?
-
Known cases where Canvas returns 200 but silently discards the DL response (schema, roles, or deployment mismatch)?
Checks done
-
RS256 signature verifies; kid matches JWKS at <JWKS_URL>
-
iss = tool issuer; aud = <CLIENT_ID>
-
deployment_id matches the request
-
data round-tripped exactly
-
Single content_item (Canvas sent accept_multiple: false)
-
Posting via form-encoded (not JSON)
Thanks for any pointers