mirror of
https://git.phreedom.club/localhost_frssoft/fediauth.git
synced 2024-11-04 15:33:20 +00:00
polish / screenshots
This commit is contained in:
parent
9f3633ea58
commit
a28f9c1cb2
46
auth.lua
46
auth.lua
|
@ -1,46 +0,0 @@
|
||||||
-- builtin auth handler
|
|
||||||
local auth_handler = minetest.get_auth_handler()
|
|
||||||
local old_get_auth = auth_handler.get_auth
|
|
||||||
|
|
||||||
-- time for otp to be enabled until properly logged in
|
|
||||||
local otp_time = 300
|
|
||||||
|
|
||||||
-- playername => start_time
|
|
||||||
local otp_sessions = {}
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
-- reset otp session upon login
|
|
||||||
local playername = player:get_player_name()
|
|
||||||
otp_sessions[player:get_player_name()] = nil
|
|
||||||
print("minetest.register_on_joinplayer(" .. playername .. ")")
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- override "get_auth" from builtin auth handler
|
|
||||||
auth_handler.get_auth = function(name)
|
|
||||||
local auth = old_get_auth(name)
|
|
||||||
|
|
||||||
print("auth_handler.get_auth(" .. name .. ")")
|
|
||||||
if name == "singleplayer" or not auth.privileges.otp_enabled then
|
|
||||||
-- singleplayer or otp not set up
|
|
||||||
return auth
|
|
||||||
end
|
|
||||||
|
|
||||||
-- minetest.disconnect_player(name, "something, something")
|
|
||||||
|
|
||||||
local now = os.time()
|
|
||||||
local otp_session = otp_sessions[name]
|
|
||||||
if not otp_session or (now - otp_session) > otp_time then
|
|
||||||
-- otp session expired or not set up
|
|
||||||
otp_sessions[name] = now
|
|
||||||
end
|
|
||||||
|
|
||||||
-- replace runtime password with legacy password hash
|
|
||||||
--auth.password = minetest.get_password_hash(name, "enter")
|
|
||||||
|
|
||||||
return auth
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_prejoinplayer(function(name)
|
|
||||||
print("minetest.register_on_prejoinplayer(" .. name .. ")")
|
|
||||||
|
|
||||||
end)
|
|
2
init.lua
2
init.lua
|
@ -13,7 +13,7 @@ otp = {
|
||||||
|
|
||||||
dofile(MP.."/functions.lua")
|
dofile(MP.."/functions.lua")
|
||||||
dofile(MP.."/onboard.lua")
|
dofile(MP.."/onboard.lua")
|
||||||
dofile(MP.."/auth.lua")
|
dofile(MP.."/join.lua")
|
||||||
dofile(MP.."/privs.lua")
|
dofile(MP.."/privs.lua")
|
||||||
|
|
||||||
if minetest.get_modpath("mtt") and mtt.enabled then
|
if minetest.get_modpath("mtt") and mtt.enabled then
|
||||||
|
|
87
join.lua
Normal file
87
join.lua
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
local FORMNAME = "otp-check"
|
||||||
|
|
||||||
|
-- time for otp code verification
|
||||||
|
local otp_time = 300
|
||||||
|
|
||||||
|
-- playername => start_time
|
||||||
|
local otp_sessions = {}
|
||||||
|
|
||||||
|
-- privs to revoke until the verification code is validated
|
||||||
|
local temp_revoke_privs = {"interact", "shout", "privs", "basic_privs", "server", "ban", "kick"}
|
||||||
|
|
||||||
|
local function revoke_privs(playername)
|
||||||
|
local privs = minetest.get_player_privs(playername)
|
||||||
|
if otp.storage:get_string(playername .. "_privs") == "" then
|
||||||
|
otp.storage:set_string(playername .. "_privs", minetest.serialize(privs))
|
||||||
|
for _, priv in ipairs(temp_revoke_privs) do
|
||||||
|
privs[priv] = nil
|
||||||
|
minetest.set_player_privs(playername, privs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function regrant_privs(playername)
|
||||||
|
local stored_priv_str = otp.storage:get_string(playername .. "_privs")
|
||||||
|
if stored_priv_str ~= "" then
|
||||||
|
local privs = minetest.deserialize(stored_priv_str)
|
||||||
|
minetest.set_player_privs(playername, privs)
|
||||||
|
otp.storage:set_string(playername .. "_privs", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Code formspec on join for otp enabled players
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
local playername = player:get_player_name()
|
||||||
|
if minetest.check_player_privs(playername, "otp_enabled") then
|
||||||
|
-- start otp session time
|
||||||
|
otp_sessions[player:get_player_name()] = os.time()
|
||||||
|
|
||||||
|
-- revoke important privs and re-grant again on code-verification
|
||||||
|
revoke_privs(playername)
|
||||||
|
|
||||||
|
-- send verification formspec
|
||||||
|
local formspec = "size[10,2]" ..
|
||||||
|
"label[1,0;Please enter your OTP code below]" ..
|
||||||
|
"field[1,1.3;4,1;code;Code;]" ..
|
||||||
|
"button_exit[5,1;3,1;submit;Verify]"
|
||||||
|
|
||||||
|
minetest.show_formspec(playername, FORMNAME, formspec)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- clear otp session on leave
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local playername = player:get_player_name()
|
||||||
|
otp_sessions[playername] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- check sessions periodically and kick if timed out
|
||||||
|
local function session_check()
|
||||||
|
local now = os.time()
|
||||||
|
for name, start_time in pairs(otp_sessions) do
|
||||||
|
if (now - start_time) > otp_time then
|
||||||
|
minetest.kick_player(name, "OTP Code validation timed out")
|
||||||
|
otp_sessions[name] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
minetest.after(5, session_check)
|
||||||
|
end
|
||||||
|
minetest.after(5, session_check)
|
||||||
|
|
||||||
|
-- otp check
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
if formname ~= FORMNAME then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
minetest.chat_send_player(playername, "OTP Code validation succeeded")
|
||||||
|
otp_sessions[playername] = nil
|
||||||
|
regrant_privs(playername)
|
||||||
|
else
|
||||||
|
minetest.kick_player(playername, "OTP Code validation failed")
|
||||||
|
end
|
||||||
|
end)
|
20
onboard.lua
20
onboard.lua
|
@ -1,22 +1,20 @@
|
||||||
local FORMNAME = "otp-enable"
|
local FORMNAME = "otp-onboard"
|
||||||
|
|
||||||
minetest.register_chatcommand("otp_disable", {
|
minetest.register_chatcommand("otp_disable", {
|
||||||
privs = { otp_enabled = true },
|
description = "Disable the otp verification",
|
||||||
|
privs = { otp_enabled = true, interact = true },
|
||||||
func = function(name)
|
func = function(name)
|
||||||
-- clear priv
|
-- clear priv
|
||||||
local privs = minetest.get_player_privs(name)
|
local privs = minetest.get_player_privs(name)
|
||||||
privs.otp_enabled = true
|
privs.otp_enabled = nil
|
||||||
minetest.set_player_privs(name, privs)
|
minetest.set_player_privs(name, privs)
|
||||||
return true, "OTP login disabled"
|
return true, "OTP login disabled"
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_chatcommand("otp_enable", {
|
minetest.register_chatcommand("otp_enable", {
|
||||||
|
description = "Enable the otp verification",
|
||||||
func = function(name)
|
func = function(name)
|
||||||
if name == "singleplayer" then
|
|
||||||
return false, "OTP not available in singleplayer"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- issuer name
|
-- issuer name
|
||||||
local issuer = "Minetest"
|
local issuer = "Minetest"
|
||||||
if minetest.settings:get("server_name") ~= "" then
|
if minetest.settings:get("server_name") ~= "" then
|
||||||
|
@ -38,9 +36,11 @@ minetest.register_chatcommand("otp_enable", {
|
||||||
end
|
end
|
||||||
|
|
||||||
local png = otp.create_qr_png(code)
|
local png = otp.create_qr_png(code)
|
||||||
local formspec = "size[10,10]" ..
|
local formspec = "size[9,10]" ..
|
||||||
"image[1,0.6;5,5;^[png:" .. minetest.encode_base64(png) .. "]" ..
|
"image[1.5,0.6;7,7;^[png:" .. minetest.encode_base64(png) .. "]" ..
|
||||||
"field[1,9;5,1;code;Code;]"
|
"label[1,7;Use the above QR code in your OTP-App to obtain a verification code]" ..
|
||||||
|
"field[1,9;4,1;code;Code;]" ..
|
||||||
|
"button_exit[5,8.7;3,1;submit;Verify]"
|
||||||
|
|
||||||
minetest.show_formspec(name, FORMNAME, formspec)
|
minetest.show_formspec(name, FORMNAME, formspec)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
minetest.register_privilege("otp_enabled", {
|
minetest.register_privilege("otp_enabled", {
|
||||||
description = "otp enabled player"
|
description = "otp enabled player",
|
||||||
|
give_to_singleplayer = false
|
||||||
})
|
})
|
36
readme.md
36
readme.md
|
@ -1,11 +1,45 @@
|
||||||
|
|
||||||
# (T)OTP mod for minetest
|
# (T)OTP mod for minetest
|
||||||
|
|
||||||
* State: **WIP**
|
* State: **Stable**
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Lets security-aware players use the `/otp_enable` command to protect their account with a second factor.
|
||||||
|
|
||||||
|
Players that have the OTP enabled have to enter a verification code upon joining the game.
|
||||||
|
|
||||||
|
# OTP Authenticator app
|
||||||
|
|
||||||
|
* https://freeotp.github.io/
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
|
||||||
|
OTP verification form
|
||||||
|
![](./screenshot1.png)
|
||||||
|
|
||||||
|
OTP Setup form
|
||||||
|
![](./screenshot2.png)
|
||||||
|
|
||||||
|
# Links / References
|
||||||
|
|
||||||
|
* https://en.wikipedia.org/wiki/Time-based_one-time_password
|
||||||
|
* https://en.wikipedia.org/wiki/HMAC-based_one-time_password
|
||||||
|
* https://en.wikipedia.org/wiki/HMAC
|
||||||
|
* https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||||
|
|
||||||
|
# Chatcommands
|
||||||
|
|
||||||
|
* `/otp_enable` Starts the OTP onboarding process
|
||||||
|
* `/otp_disable` Disables the OTP Login
|
||||||
|
|
||||||
|
# Privileges
|
||||||
|
|
||||||
|
* `otp_enabled` Players with this privilege have to verify the OTP Code upon login (automatically granted on successful `/otp_enable`)
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
* Code: `MIT`
|
* Code: `MIT`
|
||||||
|
* Textures: `CC-BY-SA 3.0`
|
||||||
* "basexx.lua" `MIT` https://github.com/aiq/basexx/blob/master/lib/basexx.lua
|
* "basexx.lua" `MIT` https://github.com/aiq/basexx/blob/master/lib/basexx.lua
|
||||||
* "qrencode.lua" `BSD` https://github.com/speedata/luaqrcode/blob/master/qrencode.lua
|
* "qrencode.lua" `BSD` https://github.com/speedata/luaqrcode/blob/master/qrencode.lua
|
BIN
screenshot1.png
Normal file
BIN
screenshot1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 393 KiB |
BIN
screenshot2.png
Normal file
BIN
screenshot2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 269 KiB |
Loading…
Reference in a new issue