{"id":35228773,"url":"https://github.com/fastly/token-functions","last_synced_at":"2025-12-30T02:02:28.067Z","repository":{"id":11519970,"uuid":"14001818","full_name":"fastly/token-functions","owner":"fastly","description":"Example implementations for Fastly's token validation","archived":false,"fork":false,"pushed_at":"2022-03-29T14:44:40.000Z","size":45,"stargazers_count":29,"open_issues_count":3,"forks_count":14,"subscribers_count":107,"default_branch":"master","last_synced_at":"2025-12-06T19:15:38.253Z","etag":null,"topics":["fastly-oss-tier2"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fastly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-10-30T22:40:26.000Z","updated_at":"2024-01-16T16:41:27.000Z","dependencies_parsed_at":"2022-08-31T16:31:26.651Z","dependency_job_id":null,"html_url":"https://github.com/fastly/token-functions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fastly/token-functions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Ftoken-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Ftoken-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Ftoken-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Ftoken-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fastly","download_url":"https://codeload.github.com/fastly/token-functions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Ftoken-functions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27667352,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-11T02:00:11.302Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastly-oss-tier2"],"created_at":"2025-12-30T02:02:25.930Z","updated_at":"2025-12-30T02:02:28.062Z","avatar_url":"https://github.com/fastly.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"### Token Authentication\n\nTokens give you the ability to create URLs that expire. If you only want to give a particular user access to a link for a specific amount of time, you'll need tokens. They're commonly used to secure video assets, but you can create and validate signatures to be transferred in other ways, like cookies or authentication headers.\n\n#### Table of Contents\n\n- How to enable Token Authentication feature\n  * [VCL](#vcl)\n- How to use\n  * [Usage](#usage)\n- Code Examples\n  * [Python](#python)\n  * [Ruby](#ruby)\n  * [PHP](#php)\n  * [Perl](#perl)\n  * [Go](#go)\n  * [C#](#c)\n  * [Java](#java)\n  * [Rust](#rust)\n  * [Javascript (Node.js)](#javascript-nodejs)\n\n#### VCL\n\nThe VCL code that enables token authentication is described in [Enabling URL token validation](https://docs.fastly.com/guides/tutorials/enabling-url-token-validation).\n\n#### Usage\n- Make sure both the VCL and client side script use the same key\n  * Change this in VCL: \"YOUR%SECRET%KEY%IN%BASE64%HERE\" to match your client side script\n  * Change this in Python: key = base64.b64decode(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\")\n- Change the following parameters, if needed\n  * In Python: \"token_lifetime\" and \"path\"\n    * If you change the \"path\", make sure you verify the VCL: req.url.path + var.token_expiration\n- Run your Python code to get your token\n  * Example: 1536399430_a6cbe4fab2f574d4d58436fa4b44bdbf765b26741\n- Test the token\n  * https://{your_fastly_domain}/{file}?token=1536399430_a6cbe4fab2f574d4d58436fa4b44bdbf765b26741\n\n#### Client Side Scripts\n\nThe client or web application will need to be able to generate tokens to authenticate with Varnish. \n\n##### Python\n\n```python\nimport hmac\nfrom hashlib import sha1\nimport time\nimport base64\n\nkey = base64.b64decode(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\")\n\ntoken_lifetime = 1209600 # 2 weeks\n\npath = \"/foo/bar.html\"\n\nexpiration = int(time.time()) + token_lifetime\n\nstring_to_sign = \"{0}{1}\".format(path,expiration)\n\ndigest = hmac.new(key, string_to_sign.encode('utf-8'), sha1)\n\nsignature = digest.hexdigest() \n\ntoken = \"{0}_{1}\".format(expiration, signature)\n\nprint(\"Token:   %s\" % token)\n```\n\n##### Ruby\n\n```ruby\nrequire 'base64'\nrequire 'openssl' \n\nkey = Base64.decode64(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\")\n\ntoken_lifetime = 1209600 # 2 weeks\n\npath = \"/foo/bar.html\"\n\nexpiration = Time.now.to_i + token_lifetime\n\nstring_to_sign = path+expiration.to_s\n\nsignature = OpenSSL::HMAC.hexdigest('sha1', key, string_to_sign)\n\ntoken = expiration.to_s + \"_\" + signature\n\nputs \"Token:   \" + token\n```\n\n##### PHP\n\n```php\n\u003c?php\n$key = base64_decode(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\");\n \n$token_lifetime = 1209600; # 2 weeks\n\n$path = \"/foo/bar.html\";\n\n$expiration = time() + $token_lifetime;\n\n$string_to_sign = $path . $expiration;\n\n$signature = hash_hmac('sha1', $string_to_sign, $key);\n\n$token = $expiration . \"_\" . $signature;\n\nprint(\"Token:   \" . $token . \"\\n\");\n?\u003e\n```\n\n##### Perl\n\n```perl\nuse MIME::Base64 'decode_base64';\nuse Digest::SHA 'hmac_sha1_hex';\n\nmy $key = decode_base64(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\");\n\nmy $token_lifetime = 1209600; # 2 weeks\n\nmy $path = \"/foo/bar.html\";\n\nmy $expiration = time() + $token_lifetime;\n\nmy $string_to_sign = $path . $expiration;\n\nmy $signature = hmac_sha1_hex($string_to_sign, $key);\n\nmy $token = \"${expiration}_${signature}\";\n\nprint \"Token:   $token\\n\";\n```\n\n##### Go\n\n```go\n// this example can be modified and run at https://play.golang.org/p/BYXqllJy_J\n\npackage main\n\nimport (\n    \"crypto/hmac\"\n    \"crypto/sha1\"\n    \"encoding/base64\"\n    \"encoding/hex\"\n    \"fmt\"\n    \"time\"\n)\n\nconst (\n    encodedKey    = \"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\"\n    tokenLifetime = 14 * 24 * time.Hour\n    path          = \"/foo/bar.html\"\n)\n\nfunc main() {\n    key, err := base64.StdEncoding.DecodeString(encodedKey)\n    if err != nil {\n        fmt.Println(\"key in not in base64: \", err)\n        return\n    }\n\n    expiration := time.Now().Add(tokenLifetime).Unix()\n\n    h := hmac.New(sha1.New, key)\n    fmt.Fprintf(h, \"%s%d\", path, expiration)\n    signature := hex.EncodeToString(h.Sum(nil))\n    token := fmt.Sprintf(\"%d_%s\", expiration, signature)\n\n    fmt.Printf(\"Token: %s\\n\", token)\n}\n```\n\n##### C♯\n\n```csharp\n// I've never written C# before. Apologies if this poor. --@stephenbasile\n\nusing System;\nusing System.Security.Cryptography;\n\nbyte[] key = Convert.FromBase64String(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\");\n\nInt32 lifetime = 1209600;\n\nstring path = \"/foo/bar.html\";\n\nInt32 expiration = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;\n\nexpiration += lifetime;\n\nstring string_to_sign = path + expiration.ToString();\n\nvar encoding = new System.Text.UTF8Encoding();\nbyte[] messageBytes = encoding.GetBytes(string_to_sign);\nusing (var hmacsha1 = new HMACSHA1(key))\n{\n\tbyte[] hashmessage = hmacsha1.ComputeHash(messageBytes);\n\tConsole.WriteLine(expiration + \"_\" + BitConverter.ToString(hashmessage).Replace(\"-\", string.Empty).ToLower());\n}\n```\n\n##### Java\n\n```\n/***\n *\n * Compile with:\n * javac token.java\n *\n * Run with:\n * java token\n * \n ***/\n\nimport java.security.SignatureException;\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.util.Base64;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\n\n\n\npublic class token {\n    private static final String HMAC_SHA256_ALGORITHM = \"HmacSHA256\";\n\n    public static void main(String[] args) throws Exception {\n        String encodedKey = \"RmFzdGx5IFRva2VuIFRlc3Q=\";\n        int    interval   = 60;\n\n        String token;\n        try {\n\n            byte[] key  = Base64.getDecoder().decode(encodedKey);\n            long number  = System.currentTimeMillis()/(interval*1000);\n            byte [] data = unpack64(number);\n\n            SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_SHA256_ALGORITHM);\n\n            Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);\n            mac.init(signingKey);\n\n            byte[] rawHmac = mac.doFinal(data);\n            token = Base64.getEncoder().encodeToString(rawHmac);\n\n        } catch (Exception e) {\n            throw new SignatureException(\"Failed to generate HMAC : \" + e.getMessage());\n        }\n\n        String response   = getUrl(\"http://token.fastly.com/token\");\n        String validation = getUrl(\"http://token.fastly.com?\"+token);\n\n        System.out.println(\"Your Token:   \"+token);\n        System.out.println(\"Fastly Token: \"+response);\n        System.out.println(\"Validation:   \"+validation);\n        \n       }\n       \n       public static byte[] unpack64(long number) {\n           ByteBuffer bb = ByteBuffer.allocate(8);\n           bb.order(ByteOrder.LITTLE_ENDIAN);\n           bb.putLong(number);\n           return bb.array();\n       }\n\n       public static String getUrl(String urlStr) throws Exception {\n           URL url = new URL(urlStr);\n           HttpURLConnection connection = null;\n           BufferedReader reader = null;\n           InputStream is = null;\n\n           try {\n               connection = (HttpURLConnection) url.openConnection();\n               is = connection.getInputStream();\n           }  catch (IOException io) {\n               is = connection.getErrorStream();\n           }\n\n           try {\n               reader = new BufferedReader(new InputStreamReader(is));\n               StringBuilder stringBuilder = new StringBuilder();\n\n\n               String line = null;\n               while ((line = reader.readLine()) != null) {\n                   stringBuilder.append(line + \"\\n\");\n               }\n               stringBuilder.setLength(stringBuilder.length() - 1);\n               return stringBuilder.toString();\n           } finally {\n               if (reader != null)\n                   reader.close();\n           }\n\n       }\n\n\n}\n```\n\n\n##### Rust\n\n```\nuse std::time::{SystemTime};\nuse hmacsha1::{hmac_sha1};\n\nfn main() {\n    let key = base64::decode(\"iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=\").unwrap();\n\n    let token_lifetime = 1209600; // 2 weeks\n\n    let path = \"/foo/bar.html\";\n\n    let expiration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() + token_lifetime;\n\n    let string_to_sign = format!(\"{}{}\", path, expiration);\n\n    let signature = hmac_sha1(\u0026key, string_to_sign.as_bytes());\n    let sighex: String = signature.iter().map(|s| format!(\"{:02x?}\", s)).collect();       \n\n    let token = format!(\"{}_{}\", expiration, sighex);\n    println!(\"token     : {}\", token);\n}\n```\n\n##### Javascript (Node.js)\n\n```javascript\n// a similar example can be tested and remixed at https://javascript-token-fastly.glitch.me/\n\nvar crypto = require('crypto');\n\nvar path = \"/foo/bar.html\";\n\nvar base64Key = 'iqFPeN2u+Z0Lm5IrsKaOFKRqEU5Gw8ePtaEkHZWuD24=';\n// The Buffer.from method decodes a base-64 encoded string. \n// Attention: don't convert key to a String or it may not work\nvar key = Buffer.from(base64Key, 'base64');\n\n// 1,209,600 seconds = 2 weeks\nvar token_lifetime = 1209600; \n// Date.now() gives the current time with a millisecond precision\nvar expiration = Math.round(Date.now()/1000 + token_lifetime);\n\nvar string_to_sign = path + String(expiration);\n\n// calculate the token and convert to HEX\nvar signature = crypto.createHmac('sha1', key).update(string_to_sign).digest('hex');\n\nvar token = String(expiration) + \"_\" + signature;\n\nconsole.log(\"Token:\", token); \n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastly%2Ftoken-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffastly%2Ftoken-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastly%2Ftoken-functions/lists"}