diff --git a/spec/helpers_spec.cr b/spec/helpers_spec.cr
index 307e52d2..215ffc76 100644
--- a/spec/helpers_spec.cr
+++ b/spec/helpers_spec.cr
@@ -1,4 +1,5 @@
 require "kemal"
+require "openssl/hmac"
 require "pg"
 require "spec"
 require "yaml"
@@ -81,4 +82,27 @@ describe "Helpers" do
       produce_comment_reply_continuation("_cE8xSu6swE", "UC1AZY74-dGVPe6bfxFwwEMg", "UgyBUaRGHB9Jmt1dsUZ4AaABAg").should eq("EiYSC19jRTh4U3U2c3dFwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd5QlVhUkdIQjlKbXQxZHNVWjRBYUFCQWciAggAKhhVQzFBWlk3NC1kR1ZQZTZiZnhGd3dFTWcyC19jRTh4U3U2c3dFQAFICg%3D%3D")
     end
   end
+
+  describe "#sign_token" do
+    it "correctly signs a given hash" do
+      token = {
+        "session" => "v1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+        "expires" => 1554680038,
+        "scopes"  => [
+          ":notifications",
+          ":subscriptions/*",
+          "GET:tokens*",
+        ],
+        "signature" => "f//2hS20th8pALF305PJFK+D2aVtvefNnQheILHD2vU=",
+      }
+      sign_token("SECRET_KEY", token).should eq(token["signature"])
+
+      token = {
+        "session"   => "v1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+        "scopes"    => [":notifications", "POST:subscriptions/*"],
+        "signature" => "fNvXoT0MRAL9eE6lTE33CEg8HitYJDOL9a22rSN2Ihg=",
+      }
+      sign_token("SECRET_KEY", token).should eq(token["signature"])
+    end
+  end
 end
diff --git a/src/invidious/users.cr b/src/invidious/users.cr
index 40f24870..ce0bd0ab 100644
--- a/src/invidious/users.cr
+++ b/src/invidious/users.cr
@@ -211,6 +211,25 @@ def create_response(user_id, operation, key, db, expire = 6.hours)
   return challenge, token
 end
 
+def sign_token(key, hash)
+  string_to_sign = [] of String
+  hash.each do |key, value|
+    if key == "signature"
+      next
+    end
+
+    case value
+    when Array
+      string_to_sign << "#{key}=#{value.sort.join(",")}"
+    else
+      string_to_sign << "#{key}=#{value}"
+    end
+  end
+
+  string_to_sign = string_to_sign.sort.join("\n")
+  return Base64.encode(OpenSSL::HMAC.digest(:sha256, key, string_to_sign)).strip
+end
+
 def validate_response(challenge, token, user_id, operation, key, db, locale)
   if !challenge
     raise translate(locale, "Hidden field \"challenge\" is a required field")