Implement DRM using Dailymotion player

In this guide, we will walk you through the different implementation steps to set-up DRMs using the Dailymotion Player.

🔒 Digital Management Right overview


What is the DRM?

DRM (Digital Rights Management) is a content protection mechanism that encrypts video streams so they can only be decrypted by authorized players.

Dailymotion uses DRM to ensure that only users with valid entitlements can access protected content.


Supported DRM Systems

Browsers and devices do not all use the same DRM technology.

The Dailymotion player supports the three industry standards to ensure maximum compatibility, and automatically selects the appropriate system based on the user's environment - no specific configuration is required on your side.

DRM SystemPrimary Platform/Browser
WidevineChrome, Firefox, Android, Edge
FairPlaySafari, iOS, macOS
PlayReadyEdge, Windows, Xbox

How does it work?

An encrypted stream cannot be played without a decryption key. Dailymotion uses a controlled authorization flow to ensure only entitled users can obtain it:

  • The video stream is encrypted by a DRM system - Widevine, FairPlay, or PlayReady depending on the user's browser or device. Without the corresponding decryption key, the stream is unreadable.

  • Your backend generates a JWT - JSON Web Token is a signed token that proves the user is entitled to watch a specific video. This is the credential the player presents to Dailymotion.

  • Dailymotion validates the JWT — it verifies the cryptographic signature, checks the video ID, and confirms the token has not expired or been used before.

  • Dailymotion issues a license — if the token is valid, the DRM server returns a license to the player. It contains the decryption key and the usage rules (when playback may start, for how long).

  • The player decrypts and renders the stream — the decryption key is used within the browser's secure CDM environment. It never becomes accessible to external code.


📘

Current Licenses usage rules

That license contains the decryption key and enforces two usage rules:

  • Relative expiration - You have 48 hours to start watching after getting the licence
  • Play duration - Once you press play, you have a 4h continuous window to finish

Note: These settings will be configurable at the creator level in future iterations.




👀 Integration overview

The integration is split into two workflows that run on every protected playback.


1. Set up your backend workflow

Start by setting up your signing infrastructure — this part includes a one-time setup and a recurring endpoint.

One-time setup:

Establish the trust relationship between your backend and Dailymotion. This is done once and never needs to be repeated.

  • Generate an RSA key pair — Create a private key to sign your tokens and a public key to share with Dailymotion. These two keys are mathematically linked: what one signs, only the other can verify.
  • Register your public key with Dailymotion — Once registered, Dailymotion can verify the authenticity of every token your backend generates. This step is required before any DRM-protected playback can work.

On every playback request:

With your keys in place, build the endpoint your frontend will call on every playback request.

  • Sign a JWT — Verify the user's entitlement, build the payload, and sign a short-lived token (5 min) with your private key.
  • Return the token to the client — The frontend will use it to configure the player.

2 - Set up your client-side workflow

Once your backend is ready, implement the player initialization sequence on your frontend. The order of these steps is strict and must not be changed.

  • Initiate the player — Create a player instance in the target container before any DRM configuration.
  • Fetch the JWT — Call your backend endpoint to retrieve the signed token.
  • Pass it to the player — Call setDrmConfig({token}) before loading any content. This step is mandatory — skipping it or calling it too late will cause the license request to fail.
  • Load the content — Call loadContent() with the video ID — the player handles the DRM system selection, the license request, and the decryption automatically.
TL;DR
<div id="player-wrapper" style="width: 640px; height: 360px;"></div>
<script src="https://geo.dailymotion.com/libs/player.js"></script>

<script>
  async function startSecurePlayback() {
    try {
      // Step 1 — Create the player
      const player = await dailymotion.createPlayer('player-wrapper');

      // Step 2 — Fetch the signed JWT from your backend
      const response = await fetch('/api/get-drm-token?videoId=x135432');
      const { token } = await response.json();

      // Step 3 — Configure DRM before loading content
      player.setDrmConfig({ token });

      // Step 4 — Load the protected video
      player.loadContent({ video: 'x135432' });

    } catch (error) {
      console.error('DRM Initialization Failed:', error);
    }
  }

  startSecurePlayback();
</script>

❗

Sequence

Note: The backend endpoint and the frontend initialization sequence must both run every time a user attempts to play a DRM-protected video.

Everything else — DRM system selection, license request, decryption — is handled automatically by the Dailymotion player.





🔧 Implement your back-end workflow


Generate your RSA keys

You must implemente a RSA keys generation from your server in order to get started.

RS256 (RSA Signature with SHA-256) is an asymmetric cryptography algorithm : it uses two different keys for two opposite operations — one to sign, one to verify.

The two keys are mathematically linked, but knowing one does not let you compute the other :

  • The Private Key holds the full secret.
  • The Public Key is derived from that secret — but contains only part of it.

You can generate them by using the following code sample:

# ── Private Key ────────────────────────────────────────────────
# Used to SIGN tokens on your server. Keep this secret.
# Output: private_key.pem
openssl genrsa -out private_key.pem 2048

# ── Public Key ─────────────────────────────────────────────────
# Derived from the private key. Used by Dailymotion to VERIFY tokens.
# Output: public_key.pem  →  share this with Dailymotion (step 3)
openssl rsa -in private_key.pem -pubout -out public_key.pem

📘

RSA Private key storage

Never store it in your codebase. Git keeps a full history — even if you delete the file later, the key remains accessible in past commits.

Instead, load it at runtime from a secret manager (AWS Secrets Manager, HashiCorp Vault) or an environment variable.


Register your RSA public key with Dailymotion

When your player requests a DRM license, it sends the signed JWT to Dailymotion's DRM server.

To validate it, Dailymotion verifies the JWT's cryptographic signature — which requires your public key. Without it, Dailymotion has no way to distinguish a legitimate token from a forged one.

Integrate your RSA public key with Dailymotion Studio

Connect to your Dailymotion Studio and select your use case:

  • Enable DRM for a specific video - Go to the Media > Video and select a video Within the video advanced settings, select Visibility. Select DRM and input your RSA public key.
  • Enable DRM for your profile - Go to Media > Content default > Visibility. Select DRM and input your RSA public key. When this is set on Content default, all VOD uploaded on this account will be DRM protected.

Build your JWT payload.


The JWT payload is a set of claims embedded in the signed token.

It defines which video is authorized, when the token was issued, and when it expires. Without it, a valid signature would only prove that a token came from a trusted source, but nothing about what it actually grants access to.

Because the payload is included in the signed data, any attempt to modify it (changing the video ID, extending the expiry) immediately invalidates the signature. This is what makes the token both tamper-proof and strictly scoped: one video, one time window, one use.


The payload is a plain JSON object with four required fields:

ClaimKeyTypeDescription
JWT IDjtistringA unique UUID generated per request. Prevents an intercepted token from being reused.
Video IDvideoIdstringBinds the token to a single video. A token for x1234533 cannot unlock any other video.
Issued AtiatintegerUnix timestamp (seconds) of the moment when the token was issued. Used by Dailymotion to validate the time window.
ExpirationexpintegerUnix timestamp (seconds) when the token expires. After this timestamp, the token is unconditionally rejected.

📘

Important notes

  • All timestamps must be in Unix Epoch Seconds.
  • A short expiration window is recommended to minimize interception risks. A window of 5 minutes (exp = iat + 300) is preferred.
  • The token must be signed using your RSA Private key using RS256 (RSA Signature with SHA-256).
  • Token generation must occur on your secure server. Never generate tokens on the client side.

const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');

/**
 * Generates a Scoped License Token for Dailymotion DRM
 * @param {string} videoId - The Dailymotion Video ID
 * @param {string} privateKey - Your RSA Private Key
 * @returns {string} Signed JWT
 */
function generateDRMToken(videoId, privateKey) {
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + 300; // 5-minute expiration window
    const jti = uuidv4();

    const payload = { 
        videoId: videoId, 
        iat: iat, 
        exp: exp, 
        jti: jti 
    };

    return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
}

Once built, the payload is passed to your signing function, it becomes the data that gets cryptographically signed with your private key.


đŸ–„ïž Implement your client-side workflow


Initiate the player

Add the player script in your webpage with a target div:

<div id="player-wrapper" style="width: 640px; height: 360px;"></div>
<script src="https://geo.dailymotion.com/libs/player.js"></script>

Create a player instance

createPlayer() is asynchronous — it returns a Promise that resolves once the player is fully initialized and ready to receive instructions. This is why await is required.

The player library locates the container by its ID and injects an iframe inside it, which hosts the player environment:

const player = await dailymotion.createPlayer('player-wrapper');

📘

The player must be ready before setDrmConfig()

Because createPlayer() is async, any instruction sent before it resolves would target an uninitialized player and be silently lost. The await guarantees that the iframe is mounted and the message channel is open before you attempt to configure DRM or load content.


Fetch the JWT from your backend and configure DRM

Call your backend endpoint to retrieve the signed token, then pass it to setDrmConfig() before loading any content:

const response = await fetch('/api/get-drm-token?videoId=x83sh87');
const { token } = await response.json(); // where token is your RS256-signed JWT returned by your backend endpoint

// Configure DRM — must happen before loadContent()
player.setDrmConfig({ token });

Load your content

Use the loadContent() method along with the video parameter, filled with the video ID:

// Replace x83sh87 with your own video ID
player.loadContent({ video: 'x83sh87' });

When the call is triggered, the following sequence of operations will start:

  • Fetch the content - The player first requests the video manifest listing all the video streams available (resolutions, bitrates, formats) and signals that the content is DRM-protected.
  • Detect the DRM system - The player inspects the manifest to determine which DRM system is required (Widevine, FairPlay, or PlayReady) based on the user's browser and device. This is automatic.
  • Request a DRM licence - The player sends a license request to Dailymotion's DRM server. This is where the token stored via setDrmConfig() is used — it is attached to the request as proof of authorization. Dailymotion verifies the token and, if valid, returns the decryption key.
  • Decrypt and play - The player uses the decryption key from the license to decrypt the video stream in real time. Playback begins.
📘

Video ID must match JWT information

The video ID must exactly match the videoId claim in your JWT. If they differ, Dailymotion's DRM server will reject the license request with a 403 Scope Mismatch error.


Complete client-side set-up example:

<div id="player-wrapper" style="width: 640px; height: 360px;"></div>
<script src="https://geo.dailymotion.com/libs/player.js"></script>

<script>
  async function startSecurePlayback() {
    try {
      // Step 1 — Create the player
      const player = await dailymotion.createPlayer('player-wrapper');

      // Step 2 — Fetch the signed JWT from your backend
      const response = await fetch('/api/get-drm-token?videoId=x135432');
      const { token } = await response.json();

      // Step 3 — Configure DRM before loading content
      player.setDrmConfig({ token });

      // Step 4 — Load the protected video
      player.loadContent({ video: 'x135432' });

    } catch (error) {
      console.error('DRM Initialization Failed:', error);
    }
  }

  startSecurePlayback();
</script>


🔍 Troubleshoot DRM


401 - Invalid Signature

Re-check your key pair — the Public Key registered with Dailymotion must match the Private Key you used to sign the token.

openssl rsa -in private_key.pem -pubout | diff - public_key.pem

401 - Token expired

Ensure exp is in seconds and (not milliseconds) and that your server time is synced.


403 - Scope mismatch

The videoId in the JWT must exactly match the video ID passed to loadContent(). Check for typos or encoding differences.


400 - Algorithm Error

Ensure the JWT header specifies RS256. HS256 will be rejected.


Playback error

Ensure setDrmConfig() is called before loadContent().


⚠ Current limitations

DRMs aren't currently compatible with the following features:

  • Recommendations — videos auto-suggested at end of playback
  • Pre-rolls — ads played before the protected video
  • AV1 codec — DRMs are only compatible with H264 at the moment

Further work will be made in the next weeks.

About recommendation, it will be available later using enable_wait_for_drm_config following the same approach as Header Bidding





Did this page help you?