aboutsummaryrefslogtreecommitdiff
path: root/lib/sasl.ml
diff options
context:
space:
mode:
authorClombrong <cromblong@egregore.fun>2025-06-11 19:24:26 +0200
committerClombrong <cromblong@egregore.fun>2025-06-11 19:24:26 +0200
commit7444155d05a63fdbad93ee6f54d92efe32f2e337 (patch)
treecd3bfba4ee81d57a3ff9a8368581622d911e117a /lib/sasl.ml
parent83b07678994d774ebc22cf387229ed7620d7708e (diff)
feat: move auth to sasl
Diffstat (limited to 'lib/sasl.ml')
-rw-r--r--lib/sasl.ml49
1 files changed, 49 insertions, 0 deletions
diff --git a/lib/sasl.ml b/lib/sasl.ml
new file mode 100644
index 0000000..606beb6
--- /dev/null
+++ b/lib/sasl.ml
@@ -0,0 +1,49 @@
+open Lwt.Syntax
+
+type auth_mechanism = PLAIN [@@deriving show { with_path = false }]
+
+type sasl_error =
+ | NotAuthorized
+ | MalformedRequest
+
+let read_sasl_error = function
+ | "not-authorized" -> NotAuthorized
+ | "malformed-request" -> MalformedRequest
+ | _ -> failwith "Unsupported SASL error returned by the server."
+
+type sasl_auth = (string option, sasl_error * (string * string) option) result
+
+let send_auth_stanza (stream, push) jid pass mechanism =
+ let gen_auth = function
+ | PLAIN -> Base64.encode_exn ("\x00" ^ jid ^ "\x00" ^ pass)
+ and parse_sasl_response stanza =
+ let open Markup in
+ let parse_additional_info = function
+ | `Text t :: _ -> Some (String.concat "" t)
+ | _ -> None
+ and parse_descriptive_text = function
+ | `Start_element ((_, "text"), [((_, "lang"), lang)]) :: `Text desc :: _ -> Some (lang, String.concat "" desc)
+ | `Start_element ((_, "text"), []) :: `Text desc :: _ -> Some ("en", String.concat "" desc)
+ | _ -> None
+ in
+ let parse_sasl_error = function
+ | `Start_element ((_, error), _) :: `End_element :: rest -> (read_sasl_error error, parse_descriptive_text rest)
+ | _ -> raise (Stream.InvalidStanza stanza)
+ in match (string stanza |> parse_xml |> signals |> to_list) with
+ | `Start_element ((_, "success"), _) :: rest -> Ok (parse_additional_info rest)
+ | `Start_element ((_, "failure"), _) :: rest -> Error (parse_sasl_error rest)
+ | _ -> raise (Stream.InvalidStanza stanza)
+ in let xmlns = "urn:ietf:params:xml:ns:xmpp-sasl" in
+ let stanza_list = [`Start_element
+ ((xmlns, "auth"),
+ [(("", "xmlns"), xmlns);
+ (("", "mechanism"), show_auth_mechanism mechanism)]);
+ `Text [gen_auth mechanism];
+ `End_element]
+ in Markup.(stanza_list |> of_list |> write_xml |> to_string) |> Option.some |> push;
+ let* response = Lwt_stream.get stream
+ in try
+ match response with
+ | Some stanza -> parse_sasl_response stanza |> Lwt.return
+ | None -> Lwt.fail Stream.ClosedStream
+ with exn -> Lwt.fail exn