> ## Documentation Index
> Fetch the complete documentation index at: https://bunnynet-cb9733c2-onclientmiddleware.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced Token Authentication

> Generate secure URLs with HMAC-SHA256 tokens, geo-restrictions, directory access, speed limits, and IP locking.

Advanced token authentication uses HMAC-SHA256 signing and supports directory-level tokens for video streaming, country-based restrictions, IP locking, speed limits, and query parameter control.

## URL structure

Signed URLs use either a [query string or path-based format](#query-string-vs-path-based-tokens):

```
https://myzone.b-cdn.net/videos/playlist.m3u8?token=HS256-abc123&expires=1598024587&token_path=%2Fvideos%2F
```

```
https://myzone.b-cdn.net/bcdn_token=HS256-abc123&expires=1598024587&token_path=%2Fvideos%2F/videos/playlist.m3u8
```

## Parameters

| Parameter                 | Required | Description                                                      |
| ------------------------- | -------- | ---------------------------------------------------------------- |
| `token`                   | Yes      | Hashed signature                                                 |
| `expires`                 | Yes      | UNIX timestamp in seconds when the URL becomes invalid           |
| `token_path`              | No       | URL-encoded path prefix for directory-level access               |
| `token_countries`         | No       | Comma-separated allowed country codes (ISO 3166-1)               |
| `token_countries_blocked` | No       | Comma-separated blocked country codes (ISO 3166-1)               |
| `token_ignore_params`     | No       | When `true`, query parameters are excluded from token validation |
| `limit`                   | No       | Download speed limit in kB/s                                     |

## Directory tokens

By default, tokens are valid only for the exact URL path. Directory tokens allow access to any file within a path prefix, essential for video streaming where players request multiple segment files.

Signing with `token_path=/videos/stream1/` allows access to all files in that directory:

```
/videos/stream1/playlist.m3u8
/videos/stream1/segment1.ts
/videos/stream1/segment2.ts
```

## IP locking

IP locking binds a token to a specific client IP address. Any request from a different IP will be rejected, even if the token is otherwise valid. This is useful for preventing token sharing or URL redistribution.

To use IP locking, pass the client's IPv4 address as the `userIp` parameter when signing the URL. The IP is included in the HMAC signature but is not appended to the URL itself.

<Note>
  Only IPv4 addresses are supported. IP locking requires the **Token IP Validation** setting to be enabled on your Pull Zone. Enabling this without including the IP in your tokens will cause all requests to fail. Enabling Token IP Validation also disables IPv6 routing on the Pull Zone to prevent mismatches.
</Note>

<Warning>
  IP locking can cause issues for users behind proxies, VPNs, or with frequently changing IP addresses (e.g. mobile networks).
</Warning>

## Expiration

By default, the token expiry is calculated as a relative offset from the current time using `expirationTime` (in seconds). If you need the URL to expire at a specific point in time, use `expiresAt` to set an absolute UTC timestamp instead. When `expiresAt` is set, `expirationTime` is ignored.

## Ignore query parameters

By default, all query string parameters present on the URL are included in the token signature. If a parameter is added, removed, or changed after signing, the token will fail validation.

Set `ignoreParams` to `true` when you need to append arbitrary query parameters to signed URLs after generation, for example, analytics tags, cache-busting parameters, or player configuration. When enabled, the `token_ignore_params=true` parameter is included in the signature instead of the actual query parameters.

## Speed limits

The `limit` parameter restricts the maximum download speed for the request in kB/s. Pass the desired speed as the `speedLimit` parameter when signing. A value of `0` means no limit.

```
https://myzone.b-cdn.net/video.mp4?token=HS256-abc123&limit=500&expires=1598024587
```

## Country restrictions

Use `token_countries` to allow access only from specific countries, or `token_countries_blocked` to allow all except specific countries (only one should be necessary per token). Country codes follow the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) format (e.g. `US`, `GB`, `DE`).

## Generate the token

```
token = "HS256-" + Base64URL(HMAC-SHA256(security_key, signature_path + expires + signing_data + user_ip))
```

Where:

* **`security_key`** is used as the HMAC key (not included in the message)
* **`signature_path`** is the URL path, or the `token_path` override if set
* **`signing_data`** is the alphabetically-sorted parameters joined as `key=value` pairs separated by `&`, excluding `token` and `expires`
* **`user_ip`** is the optional IP address (empty string if not set)

After Base64 encoding, replace `+` with `-`, `/` with `_`, and remove `=` padding characters.

## Code examples

Tested implementations are available for C#, Python, Node.js, PHP, Java, Go, and Rust in the [BunnyCDN.TokenAuthentication](https://github.com/BunnyWay/BunnyCDN.TokenAuthentication) repository.

<CodeGroup>
  ```csharp C# theme={null}
  var url = TokenSigner.SignUrl(t =>
  {
      t.Url = "https://myzone.b-cdn.net/videos/stream1/playlist.m3u8";
      t.SecurityKey = "your-security-key";
      t.ExpiresAt = DateTimeOffset.UtcNow.AddHours(1);
      t.IsDirectory = true;
      t.TokenPath = "/videos/stream1/";
      t.CountriesAllowed = new List<string> { "GB" };
  });
  ```

  ```python Python theme={null}
  from token import sign_url

  url = sign_url(
      "https://myzone.b-cdn.net/videos/stream1/playlist.m3u8",
      "your-security-key",
      expiration_time=3600,
      is_directory=True,
      path_allowed="/videos/stream1/",
      countries_allowed="GB",
  )
  ```

  ```javascript Node.js theme={null}
  const { signUrl } = require('./token');

  const url = signUrl(
      'https://myzone.b-cdn.net/videos/stream1/playlist.m3u8',
      'your-security-key',
      3600,                   // expirationTime
      '',                     // userIp
      true,                   // isDirectory
      '/videos/stream1/',     // pathAllowed
      'GB',                   // countriesAllowed
  );
  ```

  ```php PHP theme={null}
  require_once 'url_signing.php';

  $url = sign_bcdn_url(
      'https://myzone.b-cdn.net/videos/stream1/playlist.m3u8',
      'your-security-key',
      3600,                   // expiration_time
      '',                     // user_ip
      true,                   // is_directory
      '/videos/stream1/',     // path_allowed
      'GB',                   // countries_allowed
  );
  ```

  ```java Java theme={null}
  import BunnyCDN.TokenSigner;

  String url = TokenSigner.signUrl(
      "https://myzone.b-cdn.net/videos/stream1/playlist.m3u8",
      "your-security-key",
      3600,                   // expirationTime
      "",                     // userIp
      true,                   // isDirectory
      "/videos/stream1/",     // pathAllowed
      "GB",                   // countriesAllowed
      ""                      // countriesBlocked
  );
  ```

  ```go Go theme={null}
  import bunnycdn "bunnycdn-token-authentication"

  url, err := bunnycdn.SignUrl(
      "https://myzone.b-cdn.net/videos/stream1/playlist.m3u8",
      "your-security-key",
      3600,                   // expirationTime
      "",                     // userIp
      true,                   // isDirectory
      "/videos/stream1/",     // pathAllowed
      "GB",                   // countriesAllowed
      "",                     // countriesBlocked
      false,                  // ignoreParams
      nil,                    // expiresAt
      0,                      // speedLimit
  )
  ```

  ```rust Rust theme={null}
  use bunnycdn_token_authentication::sign_url;

  let url = sign_url(
      "https://myzone.b-cdn.net/videos/stream1/playlist.m3u8",
      "your-security-key",
      3600,                   // expiration_time
      "",                     // user_ip
      true,                   // is_directory
      "/videos/stream1/",     // path_allowed
      "GB",                   // countries_allowed
      "",                     // countries_blocked
      false,                  // ignore_params
      None,                   // expires_at
      0,                      // speed_limit
  )?;
  ```
</CodeGroup>

## Query string vs path-based tokens

The `isDirectory` parameter controls how the token is embedded in the URL:

* **`false` (query string)** - the token and parameters are appended as query string parameters. Suitable for direct file downloads and simple URL signing.
* **`true` (path-based)** - the token is embedded in the URL path as `/bcdn_token=...`. This is required for HLS/DASH video delivery, where the player resolves relative segment URLs against the manifest path. Placing the token in the path ensures segment requests automatically inherit authentication without modifying the player.
