ntopng/scripts/lua/admin/change_user_webauthn.lua
2026-03-19 09:25:29 +01:00

101 lines
3.5 KiB
Lua

--
-- (C) 2013-26 - ntop.org
--
-- REST endpoint for WebAuthn/Passkey credential management.
-- Actions:
-- get_registration_options -> generate challenge for registration
-- complete_registration -> verify and store a new credential
-- delete -> remove a credential
-- list -> list credentials
--
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
require "lua_utils"
local json = require "dkjson"
sendHTTPHeader('application/json')
local action = _POST["action"] or _GET["action"]
local username = _POST["username"] or _GET["username"]
if isEmptyString(username) then
print(json.encode({ result = -1, message = "Missing username" }))
return
end
username = string.lower(username)
-- Authorization: admin or the logged-in user themselves
local curr_user = _SESSION and _SESSION["user"] or ""
if (not isAdministrator()) and (curr_user ~= username) then
print(json.encode({ result = -1, message = "Not authorized" }))
return
end
if action == "get_registration_options" then
local opts = ntop.generateWebAuthnRegistrationOptions(username)
if not opts then
print(json.encode({ result = -1, message = "Failed to generate challenge" }))
return
end
-- Return options for navigator.credentials.create()
-- rp.id is intentionally empty here; the client fills it with window.location.hostname
print(json.encode({
result = 0,
challenge = opts.challenge,
rp = { name = "ntopng", id = "" },
user = { id = username, name = username, displayName = username },
pubKeyCredParams = {{ type = "public-key", alg = -7 }},
authenticatorSelection = { userVerification = "preferred", residentKey = "preferred" },
attestation = "none",
timeout = 60000
}))
elseif action == "complete_registration" then
local cred_name = _POST["cred_name"] or "Passkey"
local cred_id = _POST["cred_id"]
local client_data = _POST["client_data"]
local att_obj = _POST["att_obj"]
local challenge = _POST["challenge"]
local origin = _POST["origin"]
local rp_id = _POST["rp_id"]
if isEmptyString(cred_id) or isEmptyString(client_data) or
isEmptyString(att_obj) or isEmptyString(challenge) or
isEmptyString(origin) or isEmptyString(rp_id) then
print(json.encode({ result = -1, message = "Missing registration data" }))
return
end
local ok = ntop.completeWebAuthnRegistration(
username, cred_name, cred_id, client_data, att_obj, challenge, origin, rp_id
)
if ok then
print(json.encode({ result = 0, message = "Passkey registered successfully" }))
else
print(json.encode({ result = -1, message = "Registration verification failed" }))
end
elseif action == "delete" then
local cred_id = _POST["cred_id"]
if isEmptyString(cred_id) then
print(json.encode({ result = -1, message = "Missing credential ID" }))
return
end
local ok = ntop.deleteWebAuthnCredential(username, cred_id)
if ok then
print(json.encode({ result = 0, message = "Passkey removed" }))
else
print(json.encode({ result = -1, message = "Credential not found" }))
end
elseif action == "list" then
local creds_json = ntop.getWebAuthnCredentials(username)
-- Parse and re-encode to ensure valid JSON output
local creds = json.decode(creds_json) or {}
print(json.encode({ result = 0, credentials = creds }))
else
print(json.encode({ result = -1, message = "Unknown action" }))
end