Token authentication with "Azure Verizon Premium CDN"

This walkthrough demonstrates how to use "Azure CDN Premium from Verizon" in front of a private blob storage container as origin.

Overview

In this system, the client's browser should securely access an asset stored in a private container in blob storage, via CDN. Secure in this context means that access to the cached asset in CDN should only be granted to authorized clients, and access from the CDN to the origin should be authenticated as well. The term 'origin' usually refers to the service 'behind' the CDN, in our case an Azure Storage Account.
To implement this, the client signs in 'somehow' to the web application, using whatever is in place (step 1 below). The web application generates a 'CDN auth token', and tells the browser to fetch the asset from CDN (step 2 below). That CDN URL contains the CDN auth token in the URL's query string. The CDN endpoint (strictly speaking the CDN's point of presence (PoP) or edge node) validates the CDN auth token, and (when allowed) either serves the cached copy of the asset, or fetches the contents from the origin (step 3 below).

Create a storage account

  • Config
    • Storage account name: cdnorigi
    • Force HTTPS
    • Enable storage account keys

Create a private container named cdnfiles

  • Inside the container, create an "Stored access policy"
    • Policy identifier: cdn
    • Permissions: [Read]
    • Start time: Yesterday
    • Expiry time: Something far in the future
  • After creating the policy, go the the container's "Shared access tokens" tab
    • Signing key: Key 1 or Key 2
    • Stored access policy == cdn
    • Click "Generate SAS token and URL"
      • The Blob SAS token looks like this: si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D
      • The Blob SAS URL looks like https://cdnorigi.blob.core.windows.net/cdnfiles?si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D
  • Upload a sample file to the container
    • After uploading the file, check that the SAS actually works, by putting the filename into the middle of the SAS URL, such as
      • https://cdnorigi.blob.core.windows.net/cdnfiles/2021-01-11-Captain-Haddock-02.jpg?si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D

Create a new "Front Door and CDN profiles" offer via the Azure Portal

  • Select "Explore other offerings" (instead of the default "Azure Front Door")
  • In "Choose other offerings", pick "Azure CDN Premium from Verizon"
  • In Profile Details
    • Name: cdnchgeuer
    • Region: Global, Pricing Tier: Premium Verizon
  • Tick the "Create a new CDN endpoint" box
    • CDN Endpoint name: cdnchgeuer20230119
      • which means our public hostname will be https://cdnchgeuer20230119.azureedge.net, unless we later enable a custom domain name.
    • Origin type: "Storage"
    • Origin hostname: Pick your storage account, in my case cdnorigi.blob.core.windows.net
  • Click Create, and take a cup of coffee.
    • What now happens is that Azure in the back calls out to APIs of Edgecast/Verizon to provision a CDN profile for you. That can take a while, because Verizon under the hood will roll out your chosen hostname globally to all the various edge nodes (also called 'points of presence')
  • Configure the endpoint: Once provisioning is finished, in the resource group you should see the Storage account, the CDN profile, and the CDN endpoint:
Azure Portal Resources
In the endpoint's "Origin" settings, you might consider disabling the http port
Azure Portal CDN Origin
All the rest of the fine-grained configuration happens by navigating to "Advanced Features" and clicking the "Manage" link at the top of the page:
Azure Portal Advanced CDN Features
You now are authenticated and redirected to cdn.windowsazure.com/http/token/default.aspx, which is the place where we can configure token authentication:
Verizon Portal empty token auth configuration

Configure the HTTP Large Object Token-Based Authentication

  • In the Linux shell (for example in a bash shell in WSL), we'll be using OpenSSL to generate to high-entropy cryptographic keys:
  • Run openssl rand -hex 160 twice and note down the results. This command generates hexadecimal encoded cryptographically strong random numbers, and the number is the number of bytes. 160 bytes would be 1280 bit, encoded in 320 hex characters.
  • Prefix the first value with primary and the second one with backup, so it's easier to see which key you're looking at.
primary4c5604fb8f76524cd99cf65700fd550a92f083d297c327a50cae5891f1f6fcb6965cadaaa56682e3998e376f1cb3db757348356f73a936bfdae12c4eab07e02e70d5108593a97f267cdb2c16c87c58832270a7cfd0199ecd0808169f54238be400e7950f44bce9f801395061cf741ac1f12ea2341b8887c2b6692b6074695854834047c8bff52b3a284a51290483a985f78ad0c548d98fe306c9be3cd12605b8
backupf876ca22ccca6cf4587137251e44490a131b8cca12ab65bbdd8ffa33edeb9862f850d2050e9d3df1b2bcc72ca5ddbf3f32145c846464ce8cfadd6f36881371f88ca6942f1a685211e3eac25cb9b79bc50aad65c7b04d754637de2ee7e8429fdc25a7556e3628899a43ca94178c6d3777229b345132cada7aef4bf7f2c68bb0b04c109ece2a301486c61d5a85b5b2eaaf55ae4e06b3198ec1c9c2606f9b33c2a0
  • You can also bump up the "Minimum Encryption Version" from V2 to V3
Verizon Portal populated token auth configuration

Configure custom rules

By clicking on the "HTTP Large" menu on the top, and selecting "Rules Engine V4.0", you come to the graphical rule editing experience.
Verizon Portal empty rules
Click the +New button, and type in a new name for the draft rule, such as v1, and click "Continue"

Create a first rule, to require token authentication

  • Click the +Rule button
  • Rule Description: Require CDN Token
  • Click the + dropdown and select Match
  • The Select Category dropdown should be URL
  • The second dropdown should be URL Path Directory Wildcard
  • The Result dropdown should be Match
  • The Value is the path in the CDN URL, something like /cdnfiles/.
  • The Relative To dropdown should be Origin
  • Enforce token auth: Next, click the indented + and select Feature
    • Verizon Portal Rule 1 creation
    • Select Category dropdown == Access
    • Select Feature dropdown == Token Auth
    • Enabled == yes
image-20230119162214767
  • Name the token parameter:
    • Add another Feature, this time Access and Token Auth Parameter, and Enabled=yes
    • In the Name text box, pick the name under which your web app will convey CDN tokens to the CDN. That could be cdntoken
      Verizon Portal Rule 1 finished
Finally, save our first rule.

Create a second rule, using URL Rewriting to configure access to the private storage account container, using our SAS

  • Click +Rule to add a second rule, with a rule description of Append SAS Token or similar
  • Click the + button and again select Match, this time with General / Always
    • On the indented + button, add a Feature in the URL category, with feature being URL Rewrite
    • The Source and Destination text boxes have a little resize handle in the bottom right, you might want to make these text boxes larger to see what you're putting here.
      image-20230119163159094
  • Now we need to tell the CDN how incoming URLs should be re-written to the external origin server (Azure Blob storage in our case).
    • To know the string we need to know, temporarily click the bottom + button to add another Match, and select Origin / Customer Origin. In the Value dropdown, you should see something like this:
Verizon Portal customer ID
  • In the dropdown (marked with a 1 in the screenshot), you can see a customer origin identifier, in our case /801A0567/cdnchgeuer20230119/. Please type this text into the Source and Destination. The cdnchgeuer20230119 part in this string is the name of the endpoint which you configured earlier in the Azure portal.
  • However, the /801A0567/ looks slightly random. In practice, this is your Verizon customer ID, which you can see in the upper righthand corner of the portal (1A0567), prefixed with the 80. So in theory, you could just look at your user account info on screen, but in case that 80 prefix changes in the future, temporarily creating a customer origin rule makes it easy to lookup.
  • Now please delete the temporarily created match, by clicking the little recycle bin icon (2).
Our screen should now look like this:
Verizon Portal Rule 2 creation step 2
  • Now that our Source and Destination textboxes have the base information, we need to extend it with a mechanism to inject our shared access signature token.
  • In the Source textbox, append the name of your container, and the regular expression (.*)\?(.*) to the content, so it looks like this: /801A0567/cdnchgeuer20230119/cdnfiles/(.*)\?(.*)
    • The request coming on to the CDN looks something like this: https://cdnchgeuer20230119.azureedge.net/cdnfiles/somefile.zip?cdntoken=xxx, i.e. the path prefix /cdnfiles/, a file (somefile.zip), and the query string containing our CDN token (?cdntoken=xxx).
    • The regular expression cracks up the path around the questionmark (\?), into the file name (somefile.zip) in the first regex capture group, and the query string (cdntoken=xxx) in the second regex capture group.
  • In the Destination text box, we concatenate the following strings
    • Our base /801A0567/cdnchgeuer20230119/
    • Our storage account container name cdnfiles/
    • Then we put the file name, which is captured in the 1st capture group, by adding $1
    • The ? to start the query string again
    • Our SAS string (si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D)
    • An additional & for additional parameters
    • The second capture group $2
    • So the overall result is /801A0567/cdnchgeuer20230119/cdnfiles/$1?si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D&$2
Verizon Portal Rule 2 finished
  • Save the second rule, and click "Lock Draft as Policy"
  • In the Raw XML window, you can also see an XML representation of the rules
<policy>
<rules>
<rule>
<description>Require CDN Token</description>
<match.url.url-path-directory.wildcard result="match"
value="/cdnfiles/" ignore-case="false" relative-to="origin">
<feature.access.token-auth enabled="true"/>
<feature.access.token-auth-parameter name="cdntoken" enabled="true"/>
</match.url.url-path-directory.wildcard>
</rule>
<rule>
<description>Append SAS Token</description>
<match.always>
<feature.url.url-rewrite
source="/801A0567/cdnchgeuer20230119/cdnfiles/(.*)\?(.*)"
destination="/801A0567/cdnchgeuer20230119/cdnfiles/$1?si=cdn&amp;spr=https&amp;sv=2021-06-08&amp;sr=c&amp;sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D&amp;$2"/>
</match.always>
</rule>
</rules>
</policy>
  • Now, click the "Deploy Request" Button, select the "Production" environment, type in a deployment message (like "Deploying v1"), and "Create Deploy Request"
Lets push to prod
  • Once submitted, the Verizon system will validate the rules, after a couple of seconds, you should be seeing an "Approved" mark in the Activity log. Overall, the deployment of the rules to the various edge / PoP nodes might take a few minutes.

Create a CDN token

  • Finally, we can create a CDN token, to test our solution. We navigate back to the "HTTP Large / Token Auth" page. Further down on that page is an "Encrypt Tool" section, a small web-based UI to create a CDN token. For a simple test, we need two properties in the token, an expiration date (ec_expire) and a part of the URL we're granting access to (ec_url_allow).
  • The ec_expire header must be an epoch value, an integer which you can create using web sites such as unixtimestamp.com or epochconverter.com. For example, the timestamp "Wed Dec 31 2025 23:59:59 UTC" corresponds to the epoch value 1767225599.
  • The ec_url_ allow can contain values such as /cdnfiles.
  • Encrypting these two parameters under my primary key gives me a generated token of is9St4wZ7gTK8tuPXxKQtcmh7Bi3aMJ8b-yHxvJgyDdcJOyrePUmVIj9HXVFl1WbLWRDiuS-3RaeEgpg-JIsJT9ChNKfqis
In your solution, you (of course) won't visit the Verizon portal to generate a CDN token. You will be using your web application to (protected) generate web links pointing to CDN. So in your web solution, you need to generate these CDN tokens programatically, you certainly want to make these short-lived, limit them to a specific client, and use all the other good features.
On the .NET side, I created a small library Azure-Samples/edgecast-cdn-token-fsharp: A .NET sample to create tokens for Edgecast Azure CDN which can help you generating such tokens. In a C# solution for example, you could generate tokens like this:
using System;
using EdgecastCryptoExtensions;
var key = "primary4c5604fb8f76524cd99cf65700fd550a92f083d297c327a50cae5891f1f6fcb6965cadaaa56682e3998e376f1cb3db757348356f73a936bfdae12c4eab07e02e70d5108593a97f267cdb2c16c87c58832270a7cfd0199ecd0808169f54238be400e7950f44bce9f801395061cf741ac1f12ea2341b8887c2b6692b6074695854834047c8bff52b3a284a51290483a985f78ad0c548d98fe306c9be3cd12605b8";
var token1 =
EdgecastCrypto
.createTokenValidUntil(
new DateTime(
year: 2025, month: 12, day: 31,
hour: 23, minute: 59, second: 59))
.AddAllowedUrl("/cdnfiles/")
.Encrypt(key);
var token2 =
EdgecastCrypto
.createTokenValidFor(TimeSpan.FromDays(365.0))
//.AddAllowedCountry("DE")
// more extension methods available
.AddAllowedUrl("/cdnfiles/")
.Encrypt(key);
Console.Out.WriteLine($"Token1: {token1}");
// Token1: sU5Hu8WjfvysnYVmy6i4nsdNbQGrlnc7snQN075VgHJAj_XVnehjA - xGohmvDwCdJHGFirgBVoAoUmSdrtajA76KwB_5Hsw3
Console.Out.WriteLine($"Token2: {token2}");
// Token2: sU5Hu8WjfvysnYVmy6i4nsdNbQGrlnc7tHYI0bpZj3lAj_XVnehjA - xGohmvDwCdJHGFirgBVoD1w7xarONEKKhIGWp9nrT2

Accessing a file.

With all that information, we can now hit the CDN endpoint and access a file in our private blob storage container.
As a quick reminder, the direct URL to hit our origin server (blob storage) contained the shared access signature:
https://cdnorigi.blob.core.windows.net/cdnfiles/2021-01-11-Captain-Haddock-02.jpg?si=cdn&spr=https&sv=2021-06-08&sr=c&sig=Eod7p4YFtfp2rQX2OWT9ZJbNogCNa3Y%2FoH7O82%2F4UdI%3D
Instead of the SAS, the CDN URL contains our cdntoken:
https://cdnchgeuer20230119.azureedge.net/cdnfiles/2021-01-11-Captain-Haddock-02.jpg?cdntoken=is9St4wZ7gTK8tuPXxKQtcmh7Bi3aMJ8b-yHxvJgyDdcJOyrePUmVIj9HXVFl1WbLWRDiuS-3RaeEgpg-JIsJT9ChNKfqis
When we hit the CDN without token, or a modified one, we get a 403 - Forbidden page.
Otherwise, you can enjoy this beautiful image: