From 921f5d1c8a7a3e372f20cc1a4b0274dccf1ddc2f Mon Sep 17 00:00:00 2001 From: Benjamin Mako Hill Date: Fri, 31 Jul 2026 18:53:53 -0700 Subject: [PATCH] Add cookie-check template and saltire testing documentation The real-handshake test against the saltire LTI platform emulator turned up a launch bug dev mode cannot reach: pylti1p3's Flask adapter requires the application to supply the cookies_allowed_js_check.html template used by enable_check_cookies(), and without it every OIDC login 500s. The template tests whether cookies survive, then re-enters the login flow or offers a new-window fallback. docs/SALTIRE.md records the verified test setup: local HTTPS with a self-signed certificate, the platform description for instance/lti_config.json, the saltire-side configuration, and the session-id caveat on saltire's token/jwks URLs. The 2026-08-01 run verified the full OIDC handshake, JWT validation, NRPS roster sync over the wire, and instructor/learner role routing. Co-Authored-By: Claude Fable 5 --- .../templates/cookies_allowed_js_check.html | 33 +++++++ docs/SALTIRE.md | 85 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 coldcall_lti/templates/cookies_allowed_js_check.html create mode 100644 docs/SALTIRE.md diff --git a/coldcall_lti/templates/cookies_allowed_js_check.html b/coldcall_lti/templates/cookies_allowed_js_check.html new file mode 100644 index 0000000..238530b --- /dev/null +++ b/coldcall_lti/templates/cookies_allowed_js_check.html @@ -0,0 +1,33 @@ + + + + + {{ loading_text }} + + + +

{{ loading_text }}

+ + + + diff --git a/docs/SALTIRE.md b/docs/SALTIRE.md new file mode 100644 index 0000000..a53c158 --- /dev/null +++ b/docs/SALTIRE.md @@ -0,0 +1,85 @@ +# Testing against the saltire LTI platform emulator + +[saltire](https://saltire.lti.app) is the ceLTIc project's hosted LTI +test harness. Its Platform Emulator impersonates an LMS: it performs +the real OIDC handshake, signs a real id_token, and answers NRPS +requests, which makes it the way to exercise the app's actual launch +path without a Canvas Developer Key. This setup was first run and +verified on 2026-08-01 (launch, role routing for both Instructor and +Learner, and a live NRPS roster sync all worked). + +## Local side + +The app must be served over HTTPS, because real LTI session cookies +are `Secure; SameSite=None`. A self-signed certificate is fine — you +accept it once in the browser: + +``` +# one-time: tool keypair and TLS cert (all live in instance/, gitignored) +openssl genrsa -out instance/private.key 4096 +openssl rsa -in instance/private.key -pubout -out instance/public.key +openssl req -x509 -newkey rsa:2048 -keyout instance/tls-key.pem \ + -out instance/tls-cert.pem -days 30 -nodes -subj "/CN=localhost" + +# run +.venv/bin/python -m flask --app coldcall_lti run --port 5443 \ + --cert instance/tls-cert.pem --key instance/tls-key.pem +``` + +Then open https://localhost:5443/healthz once and click through the +certificate warning (Advanced → Proceed). + +`instance/lti_config.json` describes the saltire platform to the app. +The working shape (fill the session id — see the caveat below): + +```json +{ + "https://saltire.lti.app/platform": [ + { + "default": true, + "client_id": "saltire.lti.app", + "auth_login_url": "https://saltire.lti.app/platform/auth", + "auth_token_url": "https://saltire.lti.app/platform/token/", + "key_set_url": "https://saltire.lti.app/platform/jwks/", + "private_key_file": "private.key", + "public_key_file": "public.key", + "deployment_ids": [""] + } + ] +} +``` + +## saltire side + +On https://saltire.lti.app → Test Platform → **Security Model**: + +1. Set LTI version to **1.3.0**. The Platform Details panel then shows + the issuer, client id, deployment id, and the auth/token/jwks URLs — + copy these into `instance/lti_config.json`. +2. Set **Message URL** to `https://localhost:5443/lti/launch`. +3. Under **Tool Details**: Initiate login URL + `https://localhost:5443/lti/login`; Redirection URI(s) + `https://localhost:5443/lti/launch`; clear the tool's public keyset + URL (saltire's server cannot reach localhost) and instead paste the + contents of `instance/public.key` into the tool **Public key** box. +4. **Save**, then **Connect** launches the tool. The default saltire + user carries the Instructor role and lands on the instructor page, + with the fake "Telecommunications 101" roster synced via NRPS. To + test the student experience, change the role under **User** to + `http://purl.imsglobal.org/vocab/lis/v2/membership#Learner`, save, + and Connect again — you land on /me. + +## Caveats + +- **The token and jwks URLs embed a saltire session id** (the + `/token/s...`/`/jwks/s...` suffix). Anonymous saltire sessions get a + new id eventually, which breaks `lti_config.json` until you update + it. To keep a stable setup, use saltire's Save menu to store the + configuration under a saltire account and reconnect from there. +- saltire sometimes raises native browser dialogs (e.g. "save your + changes first" flows); nothing about that affects the tool under + test. +- The cookie-check interstitial (`cookies_allowed_js_check.html`) is + part of the app and required: pylti1p3's Flask adapter expects the + application to supply that template when `enable_check_cookies()` is + used.