Skip to content

Commit 70f1730

Browse files
bradley-ericksonDrLynch
authored andcommitted
added documentation for serving as lti
1 parent 2e0d7a4 commit 70f1730

5 files changed

Lines changed: 129 additions & 1 deletion

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0+2025.10.01T17.50.57.101Z.d6047986.master
1+
0.1.0+2025.10.01T21.16.15.146Z.8b205d0c.master

autodocs/concepts.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ implementation details:
2121
aggregated into the state our experiences depend on.
2222
- :doc:`Communication Protocol <docs/concepts/communication_protocol>` - discusses how
2323
the system queries data from reducers for dashboards.
24+
- :doc:`Student Identity Mapping <docs/concepts/student_identity_mapping>` - explain
25+
how learners information is mapped across integrations.
2426
- :doc:`Scaling <docs/concepts/scaling>` - covers strategies for growing the
2527
system once the fundamentals are in place.
2628
- :doc:`Auth <docs/concepts/auth>` - describes authentication considerations
@@ -41,6 +43,7 @@ implementation details:
4143
docs/concepts/events
4244
docs/concepts/reducers
4345
docs/concepts/communication_protocol
46+
docs/concepts/student_identity_mapping
4447
docs/concepts/scaling
4548
docs/concepts/auth
4649
docs/concepts/privacy

autodocs/how-to.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us
66
- :doc:`Communication Protocol <docs/how-to/communication_protocol>` - How to query data from reducers or system endpoints for dashboards.
77
- :doc:`Configure Learning Observer <docs/how-to/config>` - Set up credentials, environment variables, and other configuration details required for a smooth deployment.
88
- :doc:`Build Dashboards <docs/how-to/dashboards>` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring.
9+
- :doc:`LTI <docs/how-to/lti>` - Cover how to install Learning Observer as an LTI application.
910
- :doc:`Run with Docker <docs/how-to/docker>` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose.
1011
- :doc:`Writing Observer Extension <docs/how-to/extension>` - Install, configure, and validate the Writing Observer browser extension for capturing events.
1112
- :doc:`Interactive Environments <docs/how-to/interactive_environments>` - Connect Learning Observer to Jupyter and other live coding setups for iterative development.
@@ -18,6 +19,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us
1819
docs/how-to/communication_protocol.md
1920
docs/how-to/config.md
2021
docs/how-to/dashboards.md
22+
docs/how-to/lti.md
2123
docs/how-to/docker.md
2224
docs/how-to/extension.md
2325
docs/how-to/interactive_environments.md
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Student Identity Mapping
2+
3+
This document describes the current approach for reconciling a student's identity across the Google Workspace context used by Writing Observer and external platforms that only surface an email address (for example when the application is launched as an LTI tool).
4+
5+
## Why the mapping exists
6+
7+
When Writing Observer runs inside Google Workspace we naturally have access to the Google user identifier that shows up in event payloads. However, when the product is embedded as an LTI application we receive the learner's email address but do not receive the Google identifier. Many downstream reducers and dashboards expect to look students up by the Google identifier that is emitted by Google Docs events. Without an explicit bridge between those two identifiers we would be unable to join activity data with roster or profile information for LTI launches.
8+
9+
## Data sources involved
10+
11+
Two pieces of infrastructure cooperate to keep an email-to-Google-ID lookup table available:
12+
13+
1. **`student_profile` reducer** – The `student_profile` KVS pipeline in `writing_analysis.py` stores the latest email address and Google identifier (`safe_user_id`) observed for each student. The reducer only updates its state when either value changes. The resulting records live in the reducer's internal key-value namespace and therefore need to be copied to a place where other services can access them. 【F:modules/writing_observer/writing_observer/writing_analysis.py†L233-L253】
14+
2. **`map_emails_to_ids_in_kvs.py` script** – This maintenance script scans the reducer's internal keys, extracts any records that contain both `email` and `google_id`, and writes a dedicated `email-studentID-mapping:{email}` entry to the key-value store. The explicit mapping gives any service that only knows the email address a way to recover the Google identifier. 【F:scripts/map_emails_to_ids_in_kvs.py†L1-L29】
15+
16+
This flow is intentionally simple: the reducer captures whatever the client reports, and the script copies the data to keys that other components already know how to query.
17+
18+
## Operating the script
19+
20+
The email mapping script is normally run in the same environment as other KVS maintenance tasks. It requires access to the same credentials file that reducers use. A manual run looks like this:
21+
22+
```bash
23+
python scripts/map_emails_to_ids_in_kvs.py
24+
```
25+
26+
The script performs a full scan every time it runs, so it is safe to execute multiple times or to schedule as a recurring job.
27+
28+
## Limitations and future direction
29+
30+
The current reducer-plus-script approach fills an immediate gap but remains a stopgap solution:
31+
32+
* **Tight coupling to Google identity** – The reducer only records the Google identifier surfaced by Google Docs. If we ingest events from another platform, there is no canonical place to persist its identifiers.
33+
* **No user object abstraction** – Each consumer must know which KVS keys to query. A shared user object (or identity service) would allow the system to attach multiple external identifiers, roles, and profile attributes to a learner and to expose them through a stable API.
34+
* **Operational overhead** – Because the mapping lives in the KVS, we must remember to run the maintenance script anywhere we expect the lookup table to be fresh.
35+
36+
In the future we plan to introduce a formal user object that encapsulates identifiers, roles, and cross-system metadata. That abstraction would make this lookup process unnecessary by giving every component a single source of truth for student identity. Until then, this document serves as a reference for the current mapping workflow.

docs/how-to/lti.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Serve Learning Observer as an LTI 1.3 tool
2+
3+
Learning Observer ships with an IMS LTI 1.3 implementation that relies on the platform's OpenID Connect (OIDC) handshake to authenticate users and obtain scoped API access tokens from the learning management system (LMS). Use this guide to register the tool in an LMS and connect it to a course shell.
4+
5+
## Prerequisites
6+
7+
* A deployment of Learning Observer reachable by the LMS (typically via HTTPS).
8+
* An LMS that supports LTI 1.3 with dynamic registration or manual developer keys (e.g., Canvas or Schoology).
9+
* Administrative access in that LMS to create a developer key / external tool.
10+
11+
## 1. Generate and share a signing key
12+
13+
Learning Observer signs client assertions with an RSA private key when it exchanges OIDC launch data for an LMS access token. Generate a keypair, store the private key somewhere on the application host, and upload the public key to the LMS when you create the developer key.
14+
15+
```bash
16+
# create a 4096-bit RSA keypair
17+
openssl genrsa -out secrets/lti-tool-private.pem 4096
18+
openssl rsa -in secrets/lti-tool-private.pem -pubout > secrets/lti-tool-public.pem
19+
```
20+
21+
Record the filesystem path to the private key; you'll reference it in the application configuration in the next step.
22+
23+
## 2. Configure `creds.yaml`
24+
25+
Enable the LTI provider in `learning_observer/creds.yaml` by adding an entry under `auth.lti.<provider>`. Each provider requires the LMS endpoints, the Learning Observer redirect URL, and the private key location. A Canvas example looks like this:
26+
27+
```yaml
28+
auth:
29+
lti:
30+
sample-canvas:
31+
client_id: "10000000000000"
32+
auth_uri: "https://canvas.example.edu/api/lti/authorize_redirect"
33+
jwks_uri: "https://canvas.example.edu/api/lti/security/jwks"
34+
token_uri: "https://canvas.example.edu/login/oauth2/token"
35+
redirect_uri: "https://lo.example.edu/lti/sample-canvas/launch"
36+
private_key_path: "secrets/lti-tool-private.pem"
37+
api_domain: "https://canvas.example.edu" # Canvas-specific
38+
```
39+
40+
Set `redirect_uri` to the public URL that will receive the POST launch request (`/lti/<provider>/launch`). The login initiation URL for the LMS is the matching `/lti/<provider>/login` route.
41+
42+
Restart the application after updating configuration so the new provider registration is loaded.
43+
44+
## 3. Enable LMS API routes
45+
46+
Learning Observer only exposes the IMS Names & Roles (NRPS) and Assignment & Grade Service (AGS) proxy routes when the matching feature flag is turned on. Add the appropriate flag to `creds.yaml` so the application registers the routes during startup:
47+
48+
```yaml
49+
feature_flags:
50+
canvas_routes: true # Canvas NRPS/AGS proxy endpoints
51+
# schoology_routes: true # Schoology NRPS/AGS proxy endpoints
52+
```
53+
54+
## 4. Map the roster source with PMSS
55+
56+
LTI launches identify the LMS provider in the session so roster lookups can decide which backend to call. Create a PMSS overlay that maps the provider to the correct roster source (see [System settings](../concepts/system_settings.md) for more on PMSS). For Canvas, create a file such as `config/roster_source.pmss` alongside `creds.yaml` with the following contents:
57+
58+
```pmss
59+
roster_data[provider="sample-canvas"] {
60+
source: sample-canvas;
61+
}
62+
```
63+
64+
If you support multiple LMS tenants, add additional selector blocks for their email domains or provider names and point them at `schoology`, `filesystem`, or any other supported roster backend.
65+
66+
## 5. Register the tool in the LMS
67+
68+
When you create the LTI developer key/external tool inside the LMS:
69+
70+
1. Supply the **OIDC login initiation URL** as `https://<your-domain>/lti/<provider>/login`.
71+
2. Supply the **redirect/launch URL** as `https://<your-domain>/lti/<provider>/launch`.
72+
3. Paste the **public key** generated earlier so the LMS can validate the signed client assertions.
73+
4. Copy the LMS-issued **client ID** and the platform endpoints (authorize, JWKS, token) into `creds.yaml` if you have not done so already.
74+
75+
Publish the developer key and install the tool in the desired course/context. The LMS will send the context identifiers in the launch claims so Learning Observer can associate sessions with the right course.
76+
77+
## 6. Verify the launch
78+
79+
* Add the external tool link to a module or assignment inside the LMS course.
80+
* Launch the tool from within the LMS. Learning Observer should redirect the browser to the LMS's authorize endpoint, validate the launch state and nonce, and then create a session for the user when the LMS returns.
81+
* Successful launches land on the root of the application with LMS-specific authorization headers stored in the session for follow-up roster and grade sync operations.
82+
83+
If the launch fails, inspect the Learning Observer logs for messages beginning with `LTI Launch`—they include detailed context whenever state validation, token exchange, or JWT verification fails. Once the LMS recognizes the tool, you can remove or hide other authentication methods; Learning Observer will automatically expose the LTI login routes whenever `auth.lti` providers are configured.
84+
85+
## 7. Plan student identity mapping
86+
87+
LTI launch data only includes the learner's email address, while Writing Observer's Google Workspace integrations emit a Google-specific user identifier. To keep downstream reducers and dashboards working for LTI cohorts, plan to run the maintenance workflow that maps emails to Google IDs. Refer to the [Student Identity Mapping guide](../concepts/student_identity_mapping.md) for an overview of how the reducer and maintenance script cooperate and how to operate the sync in production.

0 commit comments

Comments
 (0)