Mercurial > prosody-modules
view mod_http_prebind/mod_http_prebind.lua @ 5511:0860497152af
mod_http_oauth2: Record hash of client_id to allow future verification
RFC 6819 section 5.2.2.2 states that refresh tokens MUST be bound to the
client. In order to do that, we must record something that can
definitely tie the client to the grant. Since the full client_id is so
large (why we have this client_subset function), a hash is stored
instead.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 02 Jun 2023 10:14:16 +0200 |
parents | 495a23d61418 |
children |
line wrap: on
line source
module:depends("http"); local http = require "net.http"; local format = require "util.format".format; local json_encode = require "util.json".encode; local promise = require "util.promise"; local xml = require "util.xml"; local t_insert = table.insert; local function new_options(host) return { headers = { ["Content-Type"] = "text/xml; charset=utf-8", ["Host"] = host, }, method = "POST", }; end local function connect_to_bosh(url, hostname) local rid = math.random(100000, 100000000) local options = new_options(hostname); options.body = format([[<body content='text/xml; charset=utf-8' hold='1' rid='%d' to='%s' wait='60' xml:lang='en' xmpp:version='1.0' xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh'/>]], rid, hostname); local rid = rid + 1; return promise.new(function (on_fulfilled, on_error) assert(http.request(url, options, function (body, code) if code ~= 200 then on_error("Failed to fetch, HTTP error code "..code); return; end local body = xml.parse(body); local sid = body.attr.sid; local mechanisms = {}; for mechanism in body:get_child("features", "http://etherx.jabber.org/streams") :get_child("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl") :childtags("mechanism", "urn:ietf:params:xml:ns:xmpp-sasl") do mechanisms[mechanism:get_text()] = true; end on_fulfilled({ url = url, sid = sid, rid = rid, mechanisms = mechanisms }); end)); end); end local function authenticate(data) local options = new_options(); options.body = format([[<body sid='%s' rid='%d' xmlns='http://jabber.org/protocol/httpbind'> <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/> </body>]], data.sid, data.rid); data.rid = data.rid + 1; return promise.new(function (on_fulfilled, on_error) if data.mechanisms["ANONYMOUS"] == nil then on_error("No SASL ANONYMOUS mechanism supported on this host."); return; end assert(http.request(data.url, options, function (body, code) if code ~= 200 then on_error("Failed to fetch, HTTP error code "..code); return; end local body = xml.parse(body); local success = body:get_child("success", "urn:ietf:params:xml:ns:xmpp-sasl"); if success then data.mechanisms = nil; on_fulfilled(data); else on_error("Authentication failed."); end end)); end); end; local function restart_stream(data) local options = new_options(); options.body = format([[ <body sid='%s' rid='%d' xml:lang='en' xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh' xmpp:restart='true'/>]], data.sid, data.rid); data.rid = data.rid + 1; return promise.new(function (on_fulfilled, on_error) assert(http.request(data.url, options, function (body, code) if code ~= 200 then on_error("Failed to fetch, HTTP error code "..code); return; end local body = xml.parse(body); on_fulfilled(data); end)); end); end; local function bind(data) local options = new_options(); options.body = format([[ <body sid='%s' rid='%d' xmlns='http://jabber.org/protocol/httpbind'> <iq xmlns='jabber:client' type='set'> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> </iq> </body>]], data.sid, data.rid); data.rid = data.rid + 1; return promise.new(function (on_fulfilled, on_error) assert(http.request(data.url, options, function (body, code) if code ~= 200 then on_error("Failed to fetch, HTTP error code "..code); return; end local body = xml.parse(body); local jid = body:get_child("iq", "jabber:client") :get_child("bind", "urn:ietf:params:xml:ns:xmpp-bind") :get_child_text("jid", "urn:ietf:params:xml:ns:xmpp-bind"); on_fulfilled(json_encode({rid = data.rid, sid = data.sid, jid = jid})); end)); end); end; module:provides("http", { route = { ["GET"] = function (event) return connect_to_bosh("http://[::1]:5280/http-bind", module.host) :next(authenticate) :next(restart_stream) :next(bind); end; }; });