> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Generate secure tokens for HLS/DASH

> Generate HLS and DASH secure tokens with MD5 hashing, expiration, and optional IP binding.

HLS/DASH secure tokens are generated server-side by computing an MD5 hash of the video identifier, secret key, and expiration timestamp. The backend embeds the token and expiration in the URL path before delivering the signed URL to the client. A CDN resource with [secure token protection enabled](/streaming/interaction-with-cdn/video-secure-token) is required.

## String to sign

`video_id` is the video slug for VOD or the stream ID for live streams.

Without IP binding:

```text theme={null}
${account_id}_${video_id}_${secret}_${expires}_
```

With IP binding (ties the token to the client's IP address; requests from a different IP return 403 Forbidden):

```text theme={null}
${account_id}_${video_id}_${secret}_${expires}_${user_ip}
```

| Parameter | Required | Description |
| - | - | - |
| **account\_id** | yes | Account ID |
| **video\_id** | yes | Video slug (VOD) or stream ID (live) |
| **secret** | yes | Key from CDN resource **Access** > **Secure token** settings |
| **expires** | yes | Expiration time as a Unix timestamp (UTC) |
| **user\_ip** | IP binding only | Client IP address |

The MD5 digest is encoded using unpadded Base64 URL encoding (Base64URL), where `+` and `/` are represented as `-` and `_`, and trailing `=` padding is omitted.

## Code examples

**Python**

```python theme={null}
import base64
from hashlib import md5
from time import time

def gethash(account_id, video_id, secret, expires):
    hash_body = "%s_%s_%s_%s_" % (account_id, video_id, secret, expires)
    hash_md5 = base64.b64encode(
        md5(hash_body.encode()).digest()
    ).decode().replace("+", "-").replace("/", "_").replace("=", "")
    return hash_md5

account_id = "2675"      # enter your account ID here
secret = ""              # enter your key from the CDN resource (Access > Secure token)

# VOD
video_slug = "3dk4NsRt6vWsffEr"     # enter your video slug here
expires = int(time()) + 24 * 60 * 60    # expiration = now + 24 hours

token = gethash(account_id, video_slug, secret, expires)
print(f"https://demo-protected.gvideo.io/videos/{account_id}_{video_slug}/{token}/{expires}/master.m3u8")

# LIVE
stream_id = "201693"                    # enter your stream ID here
expires = int(time()) + 24 * 60 * 60

token = gethash(account_id, stream_id, secret, expires)
print(f"https://demo-protected.gvideo.io/cmaf/{account_id}_{stream_id}/{token}/{expires}/master.m3u8")
```

**Go**

```go theme={null}
package main

import (
    "crypto/md5"
    "encoding/base64"
    "fmt"
    "time"
)

func gethash(clientId string, videoId string, secret string, expires int64) string {
    hashBody := fmt.Sprintf("%s_%s_%s_%d_", clientId, videoId, secret, expires)
    md5sum := md5.Sum([]byte(hashBody))
    hashMd5 := base64.RawURLEncoding.EncodeToString(md5sum[:])
    return hashMd5
}

func main() {
    clientId := "2675"
    secret := ""       // enter your key from the CDN resource (Access > Secure token)

    // VOD
    videoSlug := "3dk4NsRt6vWsffEr"         // enter your video slug here
    expires := time.Now().Unix() + 24*60*60 // expiration = now + 24 hours

    token := gethash(clientId, videoSlug, secret, expires)
    fmt.Printf("https://demo-protected.gvideo.io/videos/%s_%s/%s/%d/master.m3u8\n", clientId, videoSlug, token, expires)

    // LIVE
    streamId := "201693"                    // enter your stream ID here
    expires = time.Now().Unix() + 24*60*60

    token = gethash(clientId, streamId, secret, expires)
    fmt.Printf("https://demo-protected.gvideo.io/cmaf/%s_%s/%s/%d/master.m3u8\n", clientId, streamId, token, expires)
}
```
