Skip to main content

Video upload and Range streaming

Lesson video upload and Range streaming. A moderator uploads a video as a multipart request through the ingress to the LMS service, which streams it to the bucket and records the object key on the lesson. Later an enrolled founder's browser calls the stream-token endpoint with the bearer token, receives a signed token, then the video element requests the stream URL with the token as a query parameter and Range headers. The service verifies the token and returns 206 partial content read from the bucket.

Why two requests​

A <video src> element cannot add an Authorization header. The stream path is therefore public at the HTTP layer and protected by the token, which proves enrolment in the lesson's course and nothing else. The mint call is a normal authenticated request, so it runs the full checks.

Token details​

  • HS256 JWT with claims lessonId, sub (the participant) and exp.
  • Signed with a dedicated secret that never leaves the server. If no secret is configured, the mint call fails with a 500 rather than issue a token nobody can verify.
  • Valid for 6 hours by default.
  • Verification checks, in order: a secret is configured, the token has three parts, the signature matches (constant-time compare), it has not expired, and its lessonId equals the lesson being streamed. Any failure is 403.
  • The token is in the URL, so it can appear in access logs. The 6 hour lifetime limits what a leaked URL is worth.

Recordings​

Recordings use the same mechanism: GET /api/v1/lms/recordings/{id}/stream-token, then GET /api/v1/lms/recordings/{id}/stream?token=.... The enrolment check is against the recording's course.

Sources: services/lms-service/src/main/java/com/rtih/startupos/lms/{resource/LessonResource,resource/RecordingResource,service/LmsStreamTokenService,service/LessonService}.java, services/lms-service/src/main/resources/application.yml.