Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediaStore - adding pages for entitlements #1397

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/mediastore/entitlements/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"label": "Entitlements",
"position": 35,
"collapsible": true,
"collapsed": false,
"link": {
"description": "Learn how to handle entitlements with Momento MediaStore"
}
}
176 changes: 176 additions & 0 deletions docs/mediastore/entitlements/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
sidebar_position: 1
sidebar_label: About
title: Entitlements
description: Learn about media entitlements and how to use them with Momento MediaStore
hide_title: true
keywords:
- momento
- mediastore
- zero buffer rate
- zbr
- streaming
- live
- elemental
- serverless
- metrics
- entitlements
---

# Entitlements in Momento MediaStore

Entitlements are the authorization mechanisms that control what content users can access based on their subscriptions, region, or viewing rights. Managing entitlements efficiently provides a **secure**, **personalized**, and **authorized** delivery of content. Momento enables you to implement low-latency, scalable patterns that manage, distribute, and enforce entitlements dynamically in real-time, ensuring the smooth delivery of content while safeguarding rights and access.

## What are entitlements?

Entitlements define the permissions and rights a user has to view specific media content. These permissions can be based on:

* **Subscription tiers** (premium vs. free)
* **Geographic location** (regional restrictions)
* **User identity** (logged-in vs. anonymous users)
* **Device limitations** (access on mobile devices only)
* **Time-bound access** (rental or limited-time access to specific content)

:::info
Entitlements answer the critical question: *“Can this user access this content, at this time, on this device?”*
:::

## Types of access restriction

Entitlements can be based on a number of factors depending on use case. Here are some common examples.

### Subscription-based

A user’s subscription level dictates which video libraries or specific titles they can stream. For example, only premium users can access 4K streams or exclusive content.

### Geo-restrictions

Content is restricted based on the viewer's geographic location due to licensing agreements. For example, certain content is available in the US but blocked in Europe.

### Device restrictions

Certain content might be available only on specific devices. For example, a media service could provide mobile-only offers or VR-specific experiences.

### Time-sensitive access

Some content is available only for a limited time, such as live events or pay-per-view offerings that expire after viewing.

## Efficiently managing entitlements with Momento MediaStore

Entitlement checks are often resource-intensive, especially when involving complex logic that depends on user data, location, device, and content metadata. Traditional approaches often result in high database loads and latenc With Momento, you can dramatically reduce this overhead by caching entitlement data and handling real-time authorization checks with minimal delay.

## Use Momento Cache for entitlement storage

Momento Cache is an ultra-low latency cache accessible from anywhere, including browsers, media servers, and auth mechanisms. After the initial entitlement calculation for a user is performed, you can store it in the cache to provide the fastest possible experience for your viewers.

Not only is Momento Cache blazingly fast, but it also automatically scales to millions of transactions per second (TPS), leaving you without the burden of infrastructure management during stressful high-demand events. This immediate elasticity prevents traffic bottlenecks on mission-critical components of your application.

With several data types at your disposal, Momento Cache offers a flexible way to store and fetch entitlement data for access control.

### Example entitlement object

Consider the example entitlement object below.

```json
{
"subscription_level": "premium",
"region": "US",
"device_restriction": "none",
"valid_from": "2024-10-01T00:00:00Z",
"valid_until": "2024-12-31T23:59:59Z",
"allowed_content": ["movie_id_123", "series_id_456"]
}

```

This object contains information about a user that can be used to check access in a number of ways. Storing this data efficiently depends on how you evaluate entitlements at runtime.

### Storing data as a dictionary

A [cache dictionary](/cache/develop/basics/datatypes#dictionaries) allows you to store individual string and number properties and fetch them either all at once or as a subset. This provides a granular way to check entitlements if specific content is strictly region-based, tiered, etc…

Dictionaries can store an entire JSON object with a single command, providing a quick and easy way to cache entitlements after calculation. Take the example below that stores the entire entitlements object on the auth server, then evaluates access based on subscription tier on the client in the media player.

```javascript
// Auth server
const entitlements = await calculateEntitlements(userId);
await cacheClient.dictionarySetFields(NAMESPACE, userId, entitlements);

// Client side media player
let subscriptionLevel = 'free';
const subscriptionResponse = await cacheClient.dictionaryGetField(NAMESPACE, userId, 'subscription_level');
if(subscriptionResponse.type == CacheDictionaryGetFieldResponse.Hit) {
subscriptionLevel = subscriptionResponse.value();
}

const canView = subscriptionLevel === 'premium';
return canView;
```

In this example, the media player is fetching only the `subscription_level` property from the entitlements cache item, minimizing the amount of data loaded in the player.

If the entitlement evaluation is more involved and requires the entire object, it can be fetched in a single [dictionary fetch command](/cache/develop/api-reference/dictionary-collections#dictionaryfetch) instead.

### Storing data as a blob

An alternative to storing entitlements as a cache dictionary is storing it as a blob in a *scalar* cache item. This foundational object storage method will store data either as binary data or as a string.

```javascript
// Auth server
const entitlements = await calculateEntitlements(userId);
await cacheClient.set(NAMESPACE, userId, entitlements);

// Client side media player
let entitlements;
const entitlementsResponse = await cacheClient.get(NAMESPACE, userId);
if(entitlementsResponse.type == CacheGet.Hit) {
entitlements = entitlementsResponse.value():
}

const canView = await evaluate(entitlements);
return canView;
```

This example fetches the entire entitlements object out of the cache and evaluates it with more meaningful calculations based on your specific business requirements.

:::tip
Accessing scalar values can be done via the [Momento HTTP API](/cache/develop/api-reference/http-api) or through [one of the SDKs](/platform/sdks), granting cache data access to anything with internet access. Cache dictionaries are only accessible through the Momento SDK.
:::

## Restricting access with Momento auth tokens

Another way to approach entitlements is by embedding access to content directly in [session tokens](/cache/develop/authentication/tokens). After your entitlements calculation is complete and you have a known list of media a user can access, you can grant explicit access to the content via a short-lived token.

If Momento MediaStore is being used as the [media origin](/mediastore/core-concepts/origin), fine-grained access control can be used to restrict access to content upon request.

:::warning
When using a CDN to serve media, this approach does not work. This solution is for use cases that fetch content directly from Momento. Momento’s Intelligent Gateway evaluates every incoming request against the provided auth token. A CDN would not forward a particular viewer's token to Momento, which would leave this approach ineffective.
:::

Imagine your content is stored in Momento MediaStore with the following structure:

```text
/{ContentName}/playlist.m3u8
/{ContentName}/1080p/playlist.m3u8
/{ContentName}/1080p/segment1.ts
/{ContentName}/720p/playlist.m3u8
/{ContentName}/720p/segment1.ts
```

This structure implies an [HLS](/mediastore/performance/adaptive-bitrates/hls) encoded video with multiple resolutions and bitrates. All keys for this particular piece of media are prefixed with the content identifier. When creating a short-lived token, you can grant read only access to all keys for that piece of content.

```javascript
const scope = {
permissions: [ {
role: 'readonly',
cache: 'media',
item: {
keyPrefix: `/${ContentName}/`
}
}]
};

const token = authClient.generateDisposableToken(scope, ExpiresIn.hours(1), { tokenId: userId }):
```

This example creates a permission scope that grants read only access to every key beginning with the name of the content and creates a token with the Momento AuthClient valid for 1 hour with the user's id embedded in it for implicit identification. The token can then be served down the the media player to use to fetch the video directly from the origin. If the player attempts to load a video not explicitly granted in the token, it will receive an `Unauthorized` response.
106 changes: 106 additions & 0 deletions docs/mediastore/entitlements/real-time-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
sidebar_position: 2
sidebar_label: Real-time updates
title: Real-time entitlement updates
description: Learn how to keep entitlements in sync across all your media players
hide_title: true
keywords:
- momento
- mediastore
- zero buffer rate
- zbr
- streaming
- live
- elemental
- serverless
- metrics
- entitlements
---

# Making real-time updates to entitlements

When users purchase a subscription, you need to make new content immediately accessible. But if [entitlements](/mediastore/entitlements/about) are cached on client-side players, how do you invalidate it efficiently?

[Momento Topics](/topics) is a high-performance pub/sub service accessible from anywhere with tight security controls built in. When entitlements change, a message can be published to a topic to notify players of the update so they can respond immediately.

Let's take an example of a user who starts watching free tier content, but decides to upgrade her account to premium.

## 1. Entitlements are calculated and sent to the media player

Whenever the user logs in, the server calculates her entitlements, saves them in the cache, generated a session token unique for her, and passes it down to the browser making the request.

```javascript
const loginHandler = async (req, res) => {
const { userId } = req.claims;
const entitlements = calculateEntitlements(userId);
await cacheClient.set(NAMESPACE, userId, JSON.stringify(entitlements));

const scope = {
permissions: [{
role: 'readonly',
cache: NAMESPACE,
item: {
key: userId
}
},
{
role: 'subscribeonly',
cache: NAMESPACE,
topic: userId
}]
};

const tokenResponse = await authClient.generateDisposableToken(scope, ExpiresIn.hours(1), { tokenId: userId }):
res.status(200).json({ token: tokenResponse.authToken, userId }):
}
```

The generated token allows the caller to read the entitlements object in the cache and to subscribe to a topic specific to the user.

## 2. Fetch entitlements and subscribe for updates

The user's browser now has a token that allows her to get what she needs to view free tier content and subscribe for updates if the entitlements change. Below is an excerpt of the code running in the browser (this could be a web page or in a media player component).

```javascript
// Call the loginHandler from the first step
const data = await login();
cacheClient = new CacheClient({ defaultTtlSeconds: 60, credentialProvider: CredentialProvider.fromString(data.token)}):
topicClient = new TopicClient({ credentialProvider: CredentialProvider.fromString(data.token)});

await topicClient.subscribe(NAMESPACE, data.userId, {
onItem: (message) => updateEntitlements(data.userId)
});

updateEntitlements(data.userId);

function updateEntitlements(userId) {
const entitlementsResponse = await cacheClient.get(NAMESPACE, userId):

// set globally scoped entitlements object
entitlements = entitlementsResponse.value();
displayAvailableMedia();
}
```

Here, the browser is initializing both a Momento CacheClient and TopicClient with the token returned from the login API call. After the clients are created, it subscribes to the user-specific topic and fetches the entitlements out of the cache.

Whenever a message is received on the user topic, it calls a function to refresh the entitlements from the cache.

## 3. Update entitlements and notify

After watching a few videos, the user decides she would like to become a premium tier member. She goes through the checkout process and post checkout, the following code runs.

```javascript

const updateEntitlements = async (userId) => {
// Do business logic to recalculate entitlements
const newEntitlements = calculate(userId);

await cacheClient.set(NAMESPACE, userId, JSON.stringify(newEntitlements));
await topicClient.publish(NAMESPACE, userId, JSON.stringify(newEntitlements))
}
```

This code snippet runs some business logic to recalculate what the entitlements look like, then store the updated object in the cache. After the entitlements are updated, it publishes a message to the user-specific topic containing the updated entitlements.

This will signal to the connected browser to refresh the entitlements it has with the data out of Momento Cache, making sure it is immediately up to date with the changes. No additional work needs to be done! It's as easy as that!
Loading