local usermanager = require "core.usermanager" local httpserver = require "net.httpserver" local jidutil = require "util.jid" local hmac = require "hmac" local base64 = require "util.encodings".base64 local humane = require "util.serialization".serialize -- Configuration local base = "openid" local openidns = "http://specs.openid.net/auth/2.0" -- [#4.1.2] local response_404 = { status = "404 Not Found", body = "
Open ID Authentication
Identifier: %s
Realm: %s
Return: %s
]], params["openid.claimed_id"], params["openid.realm"], params["openid.return_to"], base, params["openid.return_to"]) return string.format([[ %s %s ]], head, body) elseif params["openid.mode"] == "checkid_immediate" then -- Check ID Immediate mode [#9.3] -- TODO implement check id immediate end else -- not asserting an identifier [#9.1] -- used for extensions -- TODO implement common extensions end elseif params["openid.mode"] == "check_authentication" then module:log("debug", "OpenID Check Authentication Mode") local assoc = associations[params["openid.assoc_handle"]] module:log("debug", "Checking Association Handle: "..params["openid.assoc_handle"]) if assoc and not assoc["shared"] then module:log("debug", "Found valid association") local sig = sign(params, assoc["key"]) local is_valid = "false" if sig == params["openid.sig"] then is_valid = "true" end module:log("debug", "Signature is: "..is_valid) openidresponse = { ns = openidns, is_valid = is_valid, } -- Delete this association associations[params["openid.assoc_handle"]] = nil return { headers = { ["Content-Type"] = "text/plain" }, body = tokvstring(openidresponse), } else module:log("debug", "No valid association") -- TODO return error -- Invalidate the handle [#11.4.2.2] end else -- Some other mode -- TODO error end elseif params["password"] then -- User is authenticating local user, domain = jidutil.split(params["jid"]) module:log("debug", "Authenticating "..params["jid"].." ("..user..","..domain..") with password: "..params["password"]) local valid = usermanager.validate_credentials(domain, user, params["password"], "PLAIN") if valid then module:log("debug", "Authentication Succeeded: "..params["jid"]) if params["openid.return_to"] ~= "" then -- TODO redirect the user to return_to with the openid response -- included, need to handle the case if its a GET, that there are -- existing query parameters on the return_to URL [#10.1] local host, port = split_host_port(request.headers.host) local endpointurl = "" if port == '' then endpointurl = string.format("http://%s/%s", host, base) else endpointurl = string.format("http://%s:%s/%s", host, port, base) end local nonce = nonce() local key = genkey(32) local assoc_handle = newassoc(key) local openidresponse = { ["openid.ns"] = openidns, ["openid.mode"] = "id_res", ["openid.op_endpoint"] = endpointurl, ["openid.claimed_id"] = endpointurl.."/"..user, ["openid.identity"] = endpointurl.."/"..user, ["openid.return_to"] = params["openid.return_to"], ["openid.response_nonce"] = nonce, ["openid.assoc_handle"] = assoc_handle, ["openid.signed"] = "op_endpoint,identity,claimed_id,return_to,assoc_handle,response_nonce", -- FIXME ["openid.sig"] = nil, } openidresponse["openid.sig"] = sign(openidresponse, key) queryresponse = toquerystring(openidresponse) redirecturl = params["openid.return_to"] -- add the parameters to the return_to if redirecturl:match("?") then redirecturl = redirecturl.."&" else redirecturl = redirecturl.."?" end redirecturl = redirecturl..queryresponse module:log("debug", "Open ID Positive Assertion Response Table:\n"..humane(openidresponse)) module:log("debug", "Open ID Positive Assertion Response URL:\n"..queryresponse) module:log("debug", "Redirecting User to:\n"..redirecturl) return { status = "303 See Other", headers = { Location = redirecturl, }, body = "Redirecting to: "..redirecturl -- TODO Include a note with a hyperlink to redirect } else -- TODO Do something useful is there is no return_to end else module:log("debug", "Authentication Failed: "..params["jid"]) -- TODO let them try again end else -- Not an Open ID request, do something useful -- TODO end return response_404 end local function handle_identifier(method, body, request, id) module:log("debug", "Request at OpenID identifier") local host, port = split_host_port(request.headers.host) local user_name = "" local user_domain = "" local apos = string.find(id, "@") if apos == nil then user_name = id user_domain = host else user_name = string.sub(id, 0, apos-1) user_domain = string.sub(id, apos+1) end user, domain = jidutil.split(id) local exists = usermanager.user_exists(user_name, user_domain) if not exists then return response_404 end local endpointurl = "" if port == '' then endpointurl = string.format("http://%s/%s", host, base) else endpointurl = string.format("http://%s:%s/%s", host, port, base) end local head = string.format("%s
', content) local data = string.format([[ %s %s ]], head, body) return data; end local function handle_request(method, body, request) module:log("debug", "Received request") -- Make sure the host is enabled local host = split_host_port(request.headers.host) if not hosts[host] then return response_404 end if request.url.path == "/"..base then -- OpenID Provider Endpoint return handle_endpoint(method, body, request) else local id = request.url.path:match("^/"..base.."/(.+)$") if id then -- OpenID Identifier return handle_identifier(method, body, request, id) else return response_404 end end end httpserver.new{ port = 5280, base = base, handler = handle_request, ssl = false}