How to make external applications (e.g. LMS, Tobira) work with Opencast static file authorization? #5334
Replies: 9 comments 17 replies
-
This is indeed a topic we are very interested in, as we have to deal with embedding opencast videos / player cross-origin for authorized users (and courses) on third-party websites, e.g., for research (formR, LimeSurvey), within learning modules (h5p, ...), on other web portals or LMS. Without taking a closer look, the approach of cross-site federation, SharedStorageAPI , Storage Access API or WebAuthn might be interesting for sourcing further ideas. |
Beta Was this translation helpful? Give feedback.
-
Another point against LMS/Tobira serves the files would be bandwith, as files would now be served by a (single) LMS Frontend Host instead of potentially (clustered) Opencast Presentation Host(s). In regards to LMS/Tobira sends auth info to Opencast I would strongly prefer an I'm with you on the Implementing this as a SFA Plugin does seem to make the most sense for me, as it puts the implementation closest to the related component without having to mess for global authentication/login, potentially opening unforseen side-effects with this new login method. Another important point should be how the auth-data can be generated by the LMS, assuming the Apart from implementation details this does look kind of eerily similar to how Stream Security does it. 😉 EDIT: After having read the original PR it seems like SFA and Stream-Security have slightly different goals:
|
Beta Was this translation helpful? Give feedback.
-
Adding as an additional comment as this might require a separate discussion: Having read the original PR, one of the intentions behind the SFA implementation seems to be better support for using existing OpenCast ACLs to secure its static files, whereas Stream-Security instead delegates Access-Control to the external system (eg. LMS/ILIAS). That means the SFA can be implemented in two ways: Adhere to OpenCast ACLsFor this to work there needs to be:
The former is already true for the ILIAS Plugin, but to my knowledge the later is only done for Course-Admin <-> OpenCast Cut Video/ Edit Metadata and could incure frequent changes to OpenCast Video ACLs depending on course-member fluctuations. Ignore OpenCast ACLsThis would effectively make this work exactly like Stream-Security and probably go against the design goal of SFA. Personal Assesment in regards to LMS integrationFor embedding OpenCast videos into an LMS like ILIAS the Stream-Security seems to be the better option, because it allows Access-Control to be solely manged by ILIAS which already comes with a really flexible Role-Based-Access-Control system. |
Beta Was this translation helpful? Give feedback.
-
Discussion: Multiple somewhat trusted external applications as jwt providers (trust management)Use case Question for comprehension Auth data constraints to limit scopes of authorizationI think there should be a way to limit and validate the issued jwt of somewhat trusted external applications, for example, via constraints / access rules for these jwt providers (maybe based on opencast ACL), similar to how jwt-based auth in opencast works. Example Difference between JWT-based Authentication / Authorization (OIDC) in OpencastThe difference between the usual jwt authentication / authorization setup is that we do not connect trusted identity providers providing and ensuring auth data to opencast but rather multiple somewhat trusted external applications. Do we trust them as much as, e.g., federated identity providers? Edit: |
Beta Was this translation helpful? Give feedback.
-
Cloudflare's approach to using JWTs for stream security is an applicable solution. One interesting detail is in Step 3, where they describe how the player or files can be secured using the same JWT token, which should fit our current URL schema (example below). Additionally, using JWTs as part of the request allows a web server to validate tokens and grant access to files without communicating with Opencast. For Tobira, this means that even if Opencast is offline, it can still generate valid JWTs that a web server can validate and allow access to the files. https://developers.cloudflare.com/stream/viewing-videos/securing-your-stream/ Public: Secured: Static files: |
Beta Was this translation helpful? Give feedback.
-
Technical Analysis for ILIAS:Let's say we have the ILIAS as our LMS system, and we are using Opencast: Current way of authentication/authorization:
The danger zone
LTI vs JWTImplementing custom LTI launches in ILIAS (like in Moodle) is almost impossible and the way of performing LTI launch calls is going to be banned by almost all browsers, therefore the first best and feasible option is to use is JWT. However, using that, requires some missing points/features, which, if I am not mistaken, are under constructions. What is missing/what is needed
|
Beta Was this translation helpful? Give feedback.
-
Good day everyone, sorry for not replying to this earlier. SummaryI hope the following is a fair summary of the most important points mentioned.
It seems very likely that two related PRs will be merged in the future (somewhat independently of this discussion). That's this PR which makes it possible to give access to resources directly by using special roles. The other PR will cause permissions of multiple JWTs to merge, which is also useful in a number of situations. I think with these PRs, it will be possible to solve this. However, we came here to find a standardized solution! Next I will list all points that I feel like no one disagrees with and what it seems like we are moving towards. If you disagree, let us know, but otherwise we can use these as fix-points for further discussion.
Open questions
More specific suggestionsWith us having having discussed some basic facts, let me throw some additional, more concrete suggestions out there! Don't expect anything fancy though, the following is very basic, just using common JWT claims. To grant permission to a static file managed by Opencast, the external application needs to create a signed JWT with the following fields (claims). This JWT is embedded in the URL somehow (either as query parameter or inside path).
(¹) Right now, all static files we are talking about only belong to events. But in the future, files could belong to a series or playlist (think series thumbnail). To anticipate future changes, I suggest adding this "type" prefix so that OC always knows what the JWT talks about. However, this might not be necessary since it can potentially be inferred from the request URL. Input on this would be appreciated. Additional fields can be added in the future, e.g. for restricting access based on IP ranges. We should always try to keep the JWT size as small as possible though. One noteworthy extension to this system would be to allow more than read access. That's not useful for static files, but for other uses like opening the Opencast Editor (where the user needs a session and write access to the event). With this PR, the suggested system can already be used: by adjusting the role mappings, you can "convert" the I'm looking forward to more input on the open questions and my specific suggestion. |
Beta Was this translation helpful? Give feedback.
-
One thing that I feel is mostly missing from this discussion (and might influence this) is that the scenario of an LMS telling Opencast "Trust me, xy is allowed to do this" doesn't just apply to the serving of static files, but also for more complex scenarios like sending people over to the editor, the engage player or the annotation tool and allowing them to use those tools. I wonder if for these cases, attaching rights to a user session might not be easier or even necessary. Also if we're not just talking about simple access to a resource, but want to differentiate between read and write rights or whether somebody is an annotate admin or simply allowed to annotate, it makes role mapping a lot more complex. In my naivety I imagined that we could simply encode the roles in the jwt token, but this probably makes the token much too big? (And then we have to think about whether/how to persists those rights and might want to take a look at the concept of anonymous users in Opencast that are granted specific roles by an external application, but that's probably going too far here...) Sorry for the rambling, still trying to wrap my head around a few things. I'll come back once my thoughts are a bit more refined... |
Beta Was this translation helpful? Give feedback.
-
For everyone subscribed to this discussion: I opened this PR with a proposal on how to proceed our JWT journey, as well as a specific proposal for how JWT claims should be used in the Opencast context. Please comment on that PR. I also hope to talk to many of you in person tomorrow. |
Beta Was this translation helpful? Give feedback.
-
In Opencast 9.2, a new system for static files authorization (SFA) was added. This was enabled by default in Opencast 10. Without SFA, static files (such as video files, subtitle tracks, and thumbnail images) can be accessed by anyone. With SFA enabled, Opencast authenticates and authorizes the request as with any API request, for example, and might return 403.
This is a problem for some external applications, most notably LMSs (Ilias is even mentioned in the docs) and Tobira. That's because those applications just retrieve the URL to the video file from Opencast and then tell the users browser to load that URL. But that user might not have a valid Opencast session or the browser might not send session cookies for that 3rd party request. Even if there was an Opencast session, that's not guaranteed to have the same permissions as the LMS/Tobira session. Usually the LMS/Tobira has some extra information (like course membership) that's used for authorization and that Opencast does not have.
Either way: Opencast replies 403 and the user cannot watch the video.
The goal of this discussion is to arrive at a good solution that all of these external applications can use. That way we avoid duplicate work and prevent ending up with N different solutions. So ideally, people of every LMS would participate. Feel free to state opinions, report experiences or problems, or ask questions!
CC @mtneug @ferishili
Discussion meta
This uses GitHub discussion, which features "replies". So if you want to reply to a specific comment, use the text field right below that comment and not the one at the bottom of the page. This is not a linear discussion, like a GitHub issue! If you want to discuss two or more completely unrelated aspects, consider writing multiple top level comments to keep discussions focused.
Why not just disable SFA?
That's what's done in practice by everyone right now. I don't think that's acceptable, as that makes all videos essentially public. Yes, you do need the URL to the video file, but it's not impossible to obtain that (e.g. via other users or if the video was public before). YouTube URLs are also essentially unguessable, but it is still expected that no one can watch the video if it is set to private, even if the link has been leaked. Users have the same expectation for Opencast.
Possible Solutions
The core problem is that authorization and file serving are done by two different nodes. LMS/Tobira is supposed to have authorization authority, Opencast is supposed to serve the file if the user is allowed to access it (as decided by the LMS/Tobira).
Below, I am trying to enumerate all potential solutions to this problem. It's a bit of an abstract journey so that we are all on the same page. The summary is: I think the most promising solution is using JWTs to authorize every single request.
LMS/Tobira serves the files
LMS/Tobira could rewrite URLs to static files so that they point to its own domain instead of Opencast's. That allows the LMS/Tobira to do the authorization for the incoming HTTP request for the video file. To actually respond with the file content, the LMS/Tobira could act as a reverse-proxy and pipe the request to Opencast. But that's probably slow and infeasible. Instead, the Opencast NFS could be mounted in the LMS/Tobira VM and the files would be served from there.
But that might not be possible, as Opencast and LMS/Tobira might not even be running in the same data center. It's also a bit hacky as LMS/Tobira needs to rewrite URLs, and then parse the static file paths to determine the event to check against.
LMS/Tobira sends auth info to Opencast
The LMS could send some information to Opencast that allows it to decide whether a request is authorized. That can come in many forms, but all have one thing in common: Opencast needs to be able to somehow trust the information it is getting.
Theoretical excursion
There could be a secure and direct communication channel between the LMS and Opencast. But that authorized user's HTTP request still needs to be marked somehow, in order for Opencast to identify it and distinguish it from incoming requests that are not authorized. (This is possible via IP-address or even browser fingerprinting, but that's not a good idea.) Thus, we need to add some extra data to that request. This could look like this for example:
opencast.com/files/video.mp4?id=$ID
.But since we have to add data to the HTTP request anyway (i.e. there is already a communication channel between the LMS and Opencast, albeit not secure), it would be convenient to not require this additional secure channel. We can do that by using cryptographic signatures. So:
The common solution to this is to let the LMS cryptographically sign the data in a way that Opencast can verify. That way, we can send the authdata+signature with the actual HTTP request via the user.
Create session vs. authorize single request
The LMS could send that authdata+signature to Opencast once, and Opencast would create a user session from it. That session is stored as a cookie and all further requests from the user's browser to Opencast will automatically be authorized via that session-cookie. This is exactly how LTI is currently used by some LMSs: the authdata+signature is POSTed to
/lti
. (Note: LTI is not the only way to do this! Opencast also allows creating sessions from JWTs, for example.)Unfortunately, in many situation, this requires 3rd party cookies, which are soon disallowed by all browsers. So this isn't a viable solution.
So instead, every single request that needs authorization has to include the authdata+signature. This is not supported by LTI.
What auth-data to send
With the trust aspect solved, it's still unclear what data the LMS actually sends to Opencast.
Use Opencast's role system: It might be possible to encode the LMS's authorization logic in the Opencast role-based authorization system. For example, the LMS could modify video's ACLs to include
ROLE_COURSE_123_STUDENT
. Later, when the user wants to request the video, the LMS can just send "this user has roleROLE_COURSE_123_STUDENT
". Opencast then just evaluates its own authorization scheme (checking if the user roles and the event ACLs have an overlap) and allows the request based on that. Of course, not every authorization scheme is encodable in Opencast's role system.Give access to resource directly: Since the LMS already did the authorization, it just want to tell Opencast to "allow this request to video XYZ". That way, we can completely decouple authorization from content delivery. Further, this avoids sending any user-specific data (which is good). I see two implementation options:
JWT
JWT is the most popular standard to do what I said above we want: include authdata+signature in every request and grant access to a single resource only. It defines how to pack the authdata+signature into a single string (which is base64 encoded JSON objects + signature). That string can be appended to the URL as query parameter for example.
Opencast already has a login handler for JWTs, which can retrieve the JWT from an HTTP header or from a query parameter. How to map from the arbitrary authdata (inside the JWT) to user roles is defined via configuration (role mapping).
Restricting token power
Presenting the JWT to Opencast grants you certain permissions. If the token would just say "access to video 123", that would cause two problems:
JWTs usually contain an expiration date, which states that the token is only valid before said date. That solves the second problem completely, and helps a bit with the first one. One can go further and store an IP address or other information to identify the original user inside the JWT. Opencast would then only grant permission if all those infos match that of the requester.
But that in turn means that the web application (LMS/Tobira) must deal with potentially expired/invalid JWTs. What if the user opens a video, but closes the laptop, and tries to resume watching the video in a different network ten hours later? Yes, the user can fix it by reloading the LMS page. But ideally, the LMS/Tobira frontend would detect that the JWT is not valid anymore and issue a new one automatically. We certainly don't want weird "access denied" errors appearing without good explanation. Also note that the video file is usually not downloaded in one go, but just buffered slightly ahead of the user's play position. So in general, the JWT shouldn't expire faster than the video is long or the player/frontend needs to refresh the JWT while watching.
Consequently, this will increase frontend complexity. And I think that's the main disadvantage of this solution.
Ideally, we could implement all this logic once, to avoid every LMS implementing it on their own.
Stream Security?
Yes, this all is very similar to the "stream security" feature of Opencast. I have never personally worked with that, but as far as I understand, the implementation has its fair share of problems. Further, it's not using common standards like JWT. If anyone knows more and can evaluate whether it's useful to solve our problem, please tell us!
Beta Was this translation helpful? Give feedback.
All reactions