aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wild <mwild1@gmail.com>2023-03-01 12:55:00 +0000
committerMatthew Wild <mwild1@gmail.com>2023-03-01 12:55:00 +0000
commit6db4afa0c37161bafb7986a280544a819bfa5c47 (patch)
tree946984ead954c210bac2e972244f8d226dfe447a
parentd5661f1de33307a6fd60f364705d01f6cb2195fb (diff)
downloadprosody-6db4afa0c37161bafb7986a280544a819bfa5c47.tar.gz
prosody-6db4afa0c37161bafb7986a280544a819bfa5c47.zip
util.sasl: Add SASL OAUTHBEARER mechanism (RFC 7628)
-rw-r--r--util/sasl.lua9
-rw-r--r--util/sasl/oauthbearer.lua83
2 files changed, 88 insertions, 4 deletions
diff --git a/util/sasl.lua b/util/sasl.lua
index 528743d1..1920cf21 100644
--- a/util/sasl.lua
+++ b/util/sasl.lua
@@ -133,10 +133,11 @@ function method:process(message)
end
-- load the mechanisms
-require "util.sasl.plain" .init(registerMechanism);
-require "util.sasl.anonymous" .init(registerMechanism);
-require "util.sasl.scram" .init(registerMechanism);
-require "util.sasl.external" .init(registerMechanism);
+require "util.sasl.plain" .init(registerMechanism);
+require "util.sasl.anonymous" .init(registerMechanism);
+require "util.sasl.oauthbearer" .init(registerMechanism);
+require "util.sasl.scram" .init(registerMechanism);
+require "util.sasl.external" .init(registerMechanism);
return {
registerMechanism = registerMechanism;
diff --git a/util/sasl/oauthbearer.lua b/util/sasl/oauthbearer.lua
new file mode 100644
index 00000000..ea8da198
--- /dev/null
+++ b/util/sasl/oauthbearer.lua
@@ -0,0 +1,83 @@
+local saslprep = require "util.encodings".stringprep.saslprep;
+local nodeprep = require "util.encodings".stringprep.nodeprep;
+local jid = require "util.jid";
+local json = require "util.json";
+local log = require "util.logger".init("sasl");
+local _ENV = nil;
+
+
+local function oauthbearer(self, message)
+ if not message then
+ return "failure", "malformed-request";
+ end
+
+ if message == "\001" then
+ return "failure", "not-authorized";
+ end
+
+ local gs2_authzid, kvpairs = message:match("n,a=([^,]+),(.+)$");
+ if not gs2_authzid then
+ return "failure", "malformed-request";
+ end
+
+ local auth_header;
+ for k, v in kvpairs:gmatch("([a-zA-Z]+)=([\033-\126 \009\r\n]*)\001") do
+ if k == "auth" then
+ auth_header = v;
+ break;
+ end
+ end
+
+ if not auth_header then
+ return "failure", "malformed-request";
+ end
+
+ local username = jid.prepped_split(gs2_authzid);
+
+ -- SASLprep username
+ username = saslprep(username);
+
+ if not username or username == "" then
+ log("debug", "Username violates SASLprep.");
+ return "failure", "malformed-request", "Invalid username.";
+ end
+
+ local _nodeprep = self.profile.nodeprep;
+ if _nodeprep ~= false then
+ username = (_nodeprep or nodeprep)(username);
+ if not username or username == "" then
+ return "failure", "malformed-request", "Invalid username or password."
+ end
+ end
+
+ self.username = username;
+
+ local token = auth_header:match("^Bearer (.+)$");
+
+ local correct, state, token_info = self.profile.oauthbearer(self, username, token, self.realm);
+
+ if state == false then
+ return "failure", "account-disabled";
+ elseif state == nil or not correct then
+ -- For token-level errors, RFC 7628 demands use of a JSON-encoded
+ -- challenge response upon failure. We relay additional info from
+ -- the auth backend if available.
+ return "challenge", json.encode({
+ status = token_info and token_info.status or "invalid_token";
+ scope = token_info and token_info.scope or nil;
+ ["openid-configuration"] = token_info and token_info.oidc_discovery_url or nil;
+ });
+ end
+
+ self.resource = token_info.resource;
+ self.role = token_info.role;
+ return "success";
+end
+
+local function init(registerMechanism)
+ registerMechanism("OAUTHBEARER", {"oauthbearer"}, oauthbearer);
+end
+
+return {
+ init = init;
+}