Secure your Streams with Signed URLs (Access Token)
By default, access and sharing of your Stream are available to anyone who has knowledge of its URL. This intentional accessibility is designed to facilitate seamless content sharing and embedding. Nevertheless, there are instances where it's necessary to safeguard your content and exercise precise control over who can access your Streams and for what duration. This is where our "Signed URL" feature becomes pivotal, enabling you to secure your Streams by generating signed URLs.
For users to access a signed Stream, they must authenticate themselves by providing a signed JSON Web Token (JWT) with their request. This token needs to be generated and signed with a private encryption key exclusively known to you. Optionally, this token can include an "expiration" parameter that specifies the duration of access to the signed Stream.
Getting Started:
Create a new signing key
The first step is to create a signing key by making aPOST
request to the /signing-keys
endpoint. This returns a base64 encoded private key in PEM format, which is required for signing your JSON Web Tokens.
Storing your signing key
Ensure the secure storage of your private key as we do not retain copies on our servers! If you happen to lose access to your private key, refer to the instructions provided in the Key Rotation section below to acquire a new private key.
Create a signed Stream
To protect a Stream with a Signed URL, you must set the property signed: true
when creating a new Stream. Please note that it is currently not possible to convert already existing Streams to utilize Signed URLs, nor can Signed URLs be converted back to the previous format.
Once a signed Stream is created, it cannot be accessed right away or be previewed in the Dashboard unless a JWT signed by you is provided.
Creating a JSON Web Token
JSON Web Tokens are defined by an open standard and can be generated by various third party libraries in many different programming languages. A JWT is a JSON object containing metadata, such as the expiration time of your request, and is signed with an RSA private key known only to you. Bitmovin holds the corresponding public part of your RSA key pair and uses it to verify the token's authenticity. To learn more about JSON Web Tokens, refer to this document.
By default, a signed Stream is valid for 5 hours. If you want to change that value you can set the exp
claim in the JWT to a custom value. It is recommended to choose a duration longer than your Stream's length; otherwise, the signed URL will expire prematurely, preventing you from viewing the complete video.
/**
* This example uses jose:
* https://github.com/panva/jose
*/
import * as jose from "jose";
/**
* This block represents the base64 decoded private key used for signing the JWT token.
*/
const privateKey = `-----BEGIN PRIVATE KEY-----
CONTENTS-OF-YOUR-PRIVATE-KEY-HERE
-----END PRIVATE KEY-----`;
/**
* (Optional) Specifies the expiration time in a custom format, for instance, '2h' signifies a two-hour duration.
*/
const customExpirationTime = '2h';
/**
* Algorithm used for signing the JWT, set to RSA256.
*/
const alg = 'RS256';
/**
* Converts the key string to a format required by the JOSE library.
*/
const key = await jose.importPKCS8(privateKey, alg);
/**
* Generates a base64 encoded JWT token using the provided private key and specified settings.
*/
const jwt = await new jose.SignJWT({})
.setProtectedHeader({ alg })
.setExpirationTime(customExpirationTime)
.sign(key);
# This example uses python-jose / Cryptography:
# pip install 'python-jose[cryptography]'
from jose import jwt
import time
private_key = """
-----BEGIN PRIVATE KEY-----
CONTENTS-OF-YOUR-PRIVATE-KEY-HERE
-----END PRIVATE KEY-----
"""
customExpirationTime = 2 * 60 * 60 # (Optional) Specifies the expiration time in seconds
token = {
'exp': int(time.time()) + customExpirationTime
}
json_web_token = jwt.encode(
token, private_key, algorithm="RS256")
print(json_web_token)
/*
* This example uses Auth0 Java JWT.
*
* For Gradle, add this dependency in build.gradle:
* implementation 'com.auth0:java-jwt:4.4.0'
*
* For Maven, add this dependency in your pom.xml:
* <dependency>
* <groupId>com.auth0</groupId>
* <artifactId>java-jwt</artifactId>
* <version>4.4.0</version>
* </dependency>
*
* Ensure to use the latest version available.
*/
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import java.security.KeyFactory
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.util.*
val privateKeyString = """
-----BEGIN PRIVATE KEY-----
CONTENTS-OF-YOUR-PRIVATE-KEY-HERE
-----END PRIVATE KEY-----
""".trimIndent()
fun main(args: Array<String>) {
try {
val privateKey = getPrivateKeyFromString(privateKeyString)
val twoHoursInMillis = 2 * 60 * 60 * 1000 // (Optional) Specifies the expiration time in seconds
val expirationTime = Date(System.currentTimeMillis() + twoHoursInMillis)
val algorithm = Algorithm.RSA256(null, privateKey)
val token = JWT.create()
.withExpiresAt(expirationTime)
.sign(algorithm)
println(token)
} catch (exception: Exception) {
// Invalid Signing configuration / Couldn't convert Claims.
}
}
fun getPrivateKeyFromString(key: String): RSAPrivateKey {
val cleanedPrivateKey = key
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("\\s".toRegex(), "")
val keySpec = PKCS8EncodedKeySpec(Base64.getDecoder().decode(cleanedPrivateKey))
val keyFactory = KeyFactory.getInstance("RSA")
return keyFactory.generatePrivate(keySpec) as RSAPrivateKey
}
Add Token to Streams web component
Once a token has been generated, you must add it to the Streams web component by specifying the token
parameter as follows:
<bitmovin-stream stream-id="your-stream-id" token="your-token-value"/>
Upon validation of the token, permission is granted for Stream access, initiating the playback.
Available Endpoints
Streams provides the following endpoints for managing signing keys:
POST /signing-keys
: Creates a new signing key pair and returns the private key in base64 encoding. Please note that there is a limit of 2 active signing keys per organization.
GET /signing-keys
: Retrieves your public signing key ids.
DELETE /signing-keys/{keyId}
: Deletes an existing signing key by id.
Key Rotation
In case you forgot or accidentally exposed your private key, you can rotate your signing key by following these steps:
- Create a new signing key by visiting the
/signing-keys
POST endpoint. (Limited to 2 keys) - Ensure that your new private key is set up and ready for use on your side.
- When you are confident that everything is ready with your new key, you can delete the old key via the
/signing-keys/{keyId}
DELETE
endpoint.
Limitations
Signed Streams is only currently supported via the Bitmovin CDN.
Updated 5 months ago