-
Notifications
You must be signed in to change notification settings - Fork 0
LTI integration of the Serlo Editor in edu sharing
This wiki page provides technical documentation about the integration of the Serlo Editor in the edu-sharing platform. See the repository README for general information about the integration. Edu-sharing is a learning platform which is for example deployed in the LMS of Rheinland-Pfalz. Edu-sharing offers a folder & file structure to organize and to index & search learning content.
Edu-sharing and the Serlo editor interact by following a protocol called LTI. LTI is a standard that allows a learning platform and a learning tool to exchange data. See this short introduction to LTI.
When a user on edu-sharing opens a file using the Serlo editor, edu-sharing will launch the Serlo Editor as an LTI tool. Edu-sharing acts as a LTI platform in this case. Internally, this launch will be handled by the library lti.js.
A different kind of LTI launch happens when the user want to embed content from edu-sharing (for example an image) into the content within the Serlo editor. In that case, the Serlo editor will launch edu-sharing as an LTI tool. This is called the LTI Deep Linking flow. That means, while the Serlo Editor was launched as a LTI Tool first, it now acts as a LTI Platform and launches edu-sharing as a LTI tool. Internally, no library is used and the necessary endpoints are implemented ourselves.
LTI specifies a series of messages that need to be exchanged in order to perform a LTI launch. These are not exchanged directly between the platform and tool server. Instead, the browser acts as middleman in the communication. That means, a series of messages will be sent back and forth within a browser tab or iframe using redirects or auto post forms. When this is finished, the browser tab or iframe will contain the requested resource from the tool.
Most messages in LTI are going through the browser. In order to verify the authenticity of a message, LTI uses JWT. Using the public key of the sender, the recipient of a message can verify the JWT signature. This ensures that the content originates from a trusted sender and was not tempered with.
Lti.js handles the entire launch process of opening the Serlo Editor as a LTI tool.
When the editor is successfully launched, lti.js will create a signed session cookie in the database and create a JWT called ltik. Both contain information about the platform and user id during the launch.
Each following request going to endpoints '/lti' or '/lti/...' needs to supply the session cookie and the ltik for authentication. If authentication fails, the request will not be forwarded to the request handler. See: https://cvmcosta.me/ltijs/#/provider?id=request-authentication
Before this exchange, the editor was already successfully launched as an LTI tool by edu-sharing and the user is currently inside the editor.
This flow is initiated when a user wants to embed content from edu-sharing within the Serlo Editor.
The implementation follows closely the LTI specification, see: https://www.imsglobal.org/spec/security/v1p0/#openid_connect_launch_flow, see: https://www.imsglobal.org/spec/lti-dl/v2p0
%%{init: { "sequence": { "wrap": true, "width":500 } } }%%
sequenceDiagram
participant Edusharing as Edusharing Server
participant Browser as Serlo Editor in Browser
participant Editor as Serlo Editor Server
Note over Browser: Trigger: User clicks button "embed content from edusharing" within plugin. Then, an iframe is created and the following exchange happens within.
Browser->>Editor: GET /lti/start-edusharing-deeplink-flow
Editor->>Browser: "Login Request" auto form
Browser->>Edusharing: Form is automatically submitted<br/>GET {edusharing}/rest/lti/v13/oidc/login_initiations
Edusharing->>Browser: "Authentication Request" auto form
Browser->>Editor: Form is automatically submitted<br/>GET /platform/login
Editor->>Browser: "Authentication Response containing LTI Deep Linking Request" auto form<br/> id_token is sent along with it.
Browser->>Edusharing: Form is automatically submitted<br/>POST {edusharing}/rest/lti/v13/lti13
Edusharing->>Editor: GET /platform/keys
Editor->>Edusharing: Public keys to check it_token signature
Edusharing->>Browser: Redirect to {edusharing}/components/search
Note over Browser: Edusharing presents the user with possible resources to embed. Blocks until user made a selection.
Browser->>Editor: "LTI Deep Linking Response"<br/>POST /platform/done<br/> JWT is sent along containing nodeId that was selected by user
Editor->>Edusharing: GET /edu-sharing/rest/lti/v13/jwks
Edusharing->>Editor: Public keys to check JWT
Editor->>Browser: HTML containing script that sends repositoryId and nodeId to the parent of the iframe.
Note over Browser: Iframe is closed. Editor edu-sharing plugin continues now.
Browser->>Editor: GET /lti/get-embed-html
Editor->>Edusharing: GET /edu-sharing/rest/lti/v13/details/local/{nodeId}<br/> jwt is sent along
Edusharing->>Editor: GET /platform/keys
Editor->>Edusharing: Public keys to check jwt signature
Edusharing->>Editor: HTML snippet
Editor->>Browser: HTML snippet
Note over Browser: Resource that was selected by the user is displayed within the editor plugin frame.
%%{init: { "sequence": { "wrap": true, "width":500 } } }%%
sequenceDiagram
participant Edusharing as Edusharing Server
participant Browser as Serlo Editor in Browser
participant Editor as Serlo Editor Server
Note over Browser: Trigger: User clicks button "embed content from edusharing" within plugin. Then, an iframe is created and the following exchange happens within.
Browser->>Editor: GET /lti/start-edusharing-deeplink-flow<br/>+ltik +{cookies set by lti.js during editor LTI launch}
Note over Editor: Checks: <br/>Are ltik & cookies valid? (within lti.js) <br/>Does LTI custom claim have expected type? <br/>Then: <br/>Creates session in database (deeplinkLoginData) using user, nodeId, dataToken.
Editor->>Browser: "Login Request" auto form<br/>+iss, +target_link_uri, +login_hint,<br/> +client_id=editor, +lti_deployment_id=2
Browser->>Edusharing: Form is automatically submitted<br/>GET {edusharing}/rest/lti/v13/oidc/login_initiations
Note over Edusharing: Checks: <br/>Is iss & client_id allowed to launch Edusharing as tool? <br/>Is target_link_uri the correct lti launch uri of this tool?
Edusharing->>Browser: "Authentication Request" auto form <br/>+scope=openid, +response_type=id_token, +client_id=editor, <br/>+login_hint, +state, +response_mode=form_post, <br/>+nonce, +prompt=none, +redirect_uri={edusharing}/rest/lti/v13/lti13
Note over Browser: Cookie for edu-sharing session is set.
Browser->>Editor: Form is automatically submitted<br/>GET /platform/login <br/>+{session cookies set by lti.js}
Note over Editor: Checks: <br/>Is there a valid session (deeplinkLoginData) in the database with id login_hint? <br/>Is this client_id and redirect_uri allowed to launch as a tool on this platform? <br/>Then: <br/>Add nonce to database. <br/>Create and sign id_token
Editor->>Browser: "Authentication Response containing LTI Deep Linking Request" auto form<br/>+id_token, +state
Browser->>Edusharing: Form is automatically submitted<br/>POST {edusharing}/rest/lti/v13/lti13 <br/>+{session cookies set by edu-sharing}
Note over Edusharing: Checks: <br/>Does state match what was sent? <br/>Does nonce value in id_token match what was sent? <br/>Is iss allowed?
Edusharing->>Editor: GET /platform/keys
Editor->>Edusharing: Public keys to check it_token signature
Note over Edusharing: Checks: <br/>Is id_token signature valid? <br/>
Edusharing->>Browser: Redirect to {edusharing}/components/search
Note over Browser: Edusharing presents the user with possible resources to embed. Blocks until user made a selection.
Browser->>Editor: "LTI Deep Linking Response" containing nodeId that was selected by user to embed<br/>POST /platform/done<br/>+JWT +{session cookies set by lti.js}
Note over Editor: Checks: <br/>Is nonce in JWT found in database.
Editor->>Edusharing: GET /edu-sharing/rest/lti/v13/jwks
Edusharing->>Editor: Public keys to check jwt
Note over Editor: Checks: <br/>Is JWT signature valid?
Editor->>Browser: HTML containing script that sends repositoryId and nodeId to the parent of the iframe.
Note over Browser: Iframe is closed. Editor edu-sharing plugin continues now.
Browser->>Editor: GET /lti/get-embed-html<br/>+nodeId, +repositoryId<br/>+Authorization: Bearer {ltik}, +{session cookies set by lti.js}
Editor->>Edusharing: GET /edu-sharing/rest/lti/v13/details/local/{nodeId}<br/>+displayMode=inline, +jwt
Edusharing->>Editor: GET /platform/keys
Editor->>Edusharing: Public keys to check jwt signature
Note over Edusharing: Checks: <br/>Is jwt signature valid?
Edusharing->>Editor: HTML snippet
Editor->>Browser: HTML snippet
Note over Browser: Resource that was selected by the user is displayed within the editor plugin frame.
- iss: Issuer. Who is requesting or who created a JWT.
- iat: Issued At Time.
- exp: Expiry time. Token becomes invalid at that time.
- aud: Audience. Who are the recipients of this token.
- sub: Subject in LTI. User id.
- login_hint: Unique identifier created by the serlo editor server referring to a session in the database. Should come back unmodified when receiving an Authentication Request.
- target_link_uri: Final tool uri for the LTI launch. For edu-sharing: .../edu-sharing/rest/lti/v13/lti13
- dataToken: Supplied by edu-sharing during the lti launch of the editor. Sent back to edu-sharing in some interactions. Used by edu-sharing to check for correct sender of messages. Does not have to be processed in any way outside of edu-sharing, just send it back when edu-sharing expects it.
- nodeId: Id of a file in edu-sharing. During the launch of the Serlo Editor the nodeId is the file that was opened in edu-sharing with the editor. Example: 604f62c1-6463-4206-a571-8c57097a54ae. When embedding content from edu-sharing in the editor, the nodeId will be the id of the file that you embed.
- lti_deployment_id: A unique id to a specific LIT launch.
- nonce: Value that uniquely identifies an Authentication Request. Coming from the editor server. Needs to be included in the id_token in the response to the Authentication Request. See: https://www.imsglobal.org/spec/security/v1p0/#step-2-authentication-request
- state: Used to maintain state between request and callback on the edu-sharing server. Can be used to set a cookie for edu-sharing to prevent CSRF attacks. Serlo Editor just sends it back as it is. See: https://openid.net/specs/openid-connect-core-1_0.html
- ltik: Created by lti.js after successfully launching the editor as a LTI tool. Contains information like: The user who launched the tool. The platform. The context of the launch, meaning the nodeId of the resource on edu-sharing that was opened with the editor. The ltik is a JWT signed with a secret key. Secret key is set in Provider.setup(...), see: https://cvmcosta.me/ltijs/#/provider?id=setting-up-ltijs This token is shared with the browser and used by lti.js to authenticate sessions.
Example content:
{
"platformUrl": "http://repository.127.0.0.1.nip.io:8100/edu-sharing",
"clientId": "d439...",
"deploymentId": "1",
"platformCode": "ltiaHR...",
"contextId": "http%3A%2F%2Frepository.127.0.0.1.nip.io%3A8100%2Fedu-sharingd439GQNpgPdPwbG1b72d35f7-28...",
"user": "admin",
"s": "d42f...",
"iat": 1677163358
}
- id_token: JWT that contains information about user, context, ... under which edu-sharing should be launched as a LTI tool. Also contains a deep linking request within.
Example content:
{
"iss": "http://localhost:3000/",
"aud": "editor",
"sub": "admin",
"nonce": "225c...",
"dataToken": "HU80...",
"https://purl.imsglobal.org/spec/lti/claim/deployment_id": "2",
"https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiDeepLinkingRequest",
"https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
"https://purl.imsglobal.org/spec/lti/claim/roles": [],
"https://purl.imsglobal.org/spec/lti/claim/context": {
"id": "6ae2..."
},
"https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings": {
"accept_types": [
"ltiResourceLink"
],
"accept_presentation_document_targets": [
"iframe"
],
"accept_multiple": true,
"auto_create": false,
"deep_link_return_url": "http://localhost:3000/platform/done",
"title": "",
"text": "",
"data": "63f7..."
},
"iat": 1677163372,
"exp": 1677163387
}
- JWT: JWT coming from edu-sharing. Later used to authenticate the access to a certain nodeID on edusharing. Sent within the Deep Linking response. Array content_items lists all resources on edu-sharing that should be embedded. In the example, the nodeID "b3a..." should be embedded.
Content example:
{
"iss": "editor",
"aud": "http://localhost:3000/",
"exp": 1677166987,
"iat": 1677163387,
"nonce": "225...",
"azp": "http://localhost:3000/",
"https://purl.imsglobal.org/spec/lti/claim/deployment_id": "2",
"https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiDeepLinkingResponse",
"https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
"https://purl.imsglobal.org/spec/lti-dl/claim/data": "63f...",
"https://purl.imsglobal.org/spec/lti-dl/claim/content_items": [
{
"custom": {
"repositoryId": "local",
"nodeId": "b3a..." // Resource on edu-sharing that was selected by user that will be embedded.
},
"icon": {
"width": "null",
"url": "http://repository.127.0.0.1.nip.io:8100/edu-sharing/themes/default/images/common/mime-types/svg/file-image.svg",
"height": "null"
},
"type": "ltiResourceLink",
"title": "assets.serlo.org/5a8...a.png",
"url": "http://repository.127.0.0.1.nip.io:8100/edu-sharing/rest/lti/v13/lti13/b3a..."
}
]
}
- jwt: Created & signed by the editor server and sent along when requesting the html snippet to the resource that should be embedded.
{
"aud": "editor",
"https://purl.imsglobal.org/spec/lti/claim/deployment_id": "2",
"expiresIn": 60,
"dataToken": "ji4...",
"https://purl.imsglobal.org/spec/lti/claim/context": {
"id": "editor"
},
"iat": 1677696350,
"exp": 1677696365
}
- Home
- Serlo Infrastructure
- Serlo Infrastructure for Non programmers
- Resources for new programmers
- Setup of the toolchain
- Best Practices
- Data Privacy for Devs
- How Tos
- Single Sign On
- Integration with the Data Wallet
- User Journey
- Integration of "Datenraum" into the Serlo Editor
- Introduction to the Serlo editor
- Core concepts of the Serlo editor
- Packages of the Serlo editor
- Creating a new plugin (outdated)
- Redux process in the Serlo editor
- The content format of the Serlo editor
- Serlo Editor Plugin Initial State
- How the Serlo Editor is integrated into edu-sharing via LTI
- Learner Events and xAPI