From 376d163d086adb2b0ce6d3f8bb48cee83027d86c Mon Sep 17 00:00:00 2001
From: BuckarooBanzay <BuckarooBanzay@users.noreply.github.com>
Date: Wed, 8 Feb 2023 21:40:47 +0100
Subject: [PATCH] +/-30 seconds otp validity

---
 functions.lua      | 11 +++++++++++
 functions.spec.lua | 19 +++++++++++++++++--
 join.lua           |  3 +--
 onboard.lua        |  3 +--
 4 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/functions.lua b/functions.lua
index a3dc6b0..f38edfb 100644
--- a/functions.lua
+++ b/functions.lua
@@ -199,4 +199,15 @@ function otp.is_player_enabled(name)
     local has_priv = minetest.check_player_privs(name, "otp_enabled")
 
     return has_secret and has_priv
+end
+
+function otp.check_code(secret_b32, code, time)
+    time = time or os.time()
+    for _, t_offset in ipairs({0, -30, 30}) do
+        local expected_code = otp.generate_totp(secret_b32, time + t_offset)
+        if expected_code == code then
+            return true
+        end
+    end
+    return false
 end
\ No newline at end of file
diff --git a/functions.spec.lua b/functions.spec.lua
index 7819fcb..57d7013 100644
--- a/functions.spec.lua
+++ b/functions.spec.lua
@@ -28,12 +28,12 @@ mtt.register("otp.hmac", function(callback)
 end)
 
 mtt.register("otp.generate_totp", function(callback)
-    local expected_code = 699847
+    local expected_code = "699847"
     local secret_b32 = "N6JGKMEKU2E6HQMLLNMJKBRRGVQ2ZKV7"
     local unix_time = 1640995200
 
     local code, valid_seconds = otp.generate_totp(secret_b32, unix_time)
-    assert(code == ""..expected_code)
+    assert(code == expected_code)
     assert(valid_seconds > 0)
 
     code, valid_seconds = otp.generate_totp(secret_b32)
@@ -41,6 +41,21 @@ mtt.register("otp.generate_totp", function(callback)
     callback()
 end)
 
+mtt.register("otp.check_code", function(callback)
+    local expected_code = "699847"
+    local secret_b32 = "N6JGKMEKU2E6HQMLLNMJKBRRGVQ2ZKV7"
+    local unix_time = 1640995200
+
+    assert(otp.check_code(secret_b32, expected_code, unix_time))
+    assert(otp.check_code(secret_b32, expected_code, unix_time+30))
+    assert(otp.check_code(secret_b32, expected_code, unix_time-30))
+    assert(not otp.check_code(secret_b32, expected_code, unix_time-60))
+    assert(not otp.check_code(secret_b32, expected_code, unix_time+60))
+    assert(not otp.check_code(secret_b32, expected_code))
+
+    callback()
+end)
+
 mtt.register("otp.create_qr_png", function(callback)
     local url = "otpauth://totp/abc:myaccount?algorithm=SHA1&digits=6&issuer=abc&period=30&"
         .. "secret=N6JGKMEKU2E6HQMLLNMJKBRRGVQ2ZKV7"
diff --git a/join.lua b/join.lua
index 83ef63a..305466b 100644
--- a/join.lua
+++ b/join.lua
@@ -55,8 +55,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 
     local playername = player:get_player_name()
     local secret_b32 = otp.get_player_secret_b32(playername)
-    local expected_code = otp.generate_totp(secret_b32)
-    if expected_code == fields.code then
+    if otp.check_code(secret_b32, fields.code) then
         minetest.chat_send_player(playername, "OTP Code validation succeeded")
         otp_sessions[playername] = nil
         otp.regrant_privs(playername)
diff --git a/onboard.lua b/onboard.lua
index 1f4cc78..6645c3c 100644
--- a/onboard.lua
+++ b/onboard.lua
@@ -63,8 +63,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
     if fields.code then
         local playername = player:get_player_name()
         local secret_b32 = otp.get_player_secret_b32(playername)
-        local expected_code = otp.generate_totp(secret_b32)
-        if expected_code == fields.code then
+        if otp.check_code(secret_b32, fields.code) then
             -- set priv
             local privs = minetest.get_player_privs(playername)
             privs.otp_enabled = true